fix quicClient

This commit is contained in:
anlicheng 2026-04-28 16:58:34 +08:00
parent b37bebd7b2
commit 7b549981af

View File

@ -18,6 +18,7 @@ enum SDLQUICError: Error {
case timeout case timeout
case decodeError(String) case decodeError(String)
case packetTooLarge case packetTooLarge
case waitReadyAlreadyInProgress
} }
enum SDLQUICEvent: Error { enum SDLQUICEvent: Error {
@ -56,7 +57,17 @@ actor SDLQUICClient {
// 2M // 2M
private let maxBufferSize: Int private let maxBufferSize: Int
private let readyState = SDLQUICReadyState() private enum ReadyStatus {
case idle
case connecting
case ready
case failed(Error)
case cancelled
}
private var readyStatus: ReadyStatus = .idle
private var readyContinuation: CheckedContinuation<Void, Error>?
private var readyTimeoutTask: Task<Void, Never>?
// //
public var messageStream: AsyncStream<SDLQUICInboundMessage> public var messageStream: AsyncStream<SDLQUICInboundMessage>
@ -106,22 +117,22 @@ actor SDLQUICClient {
SDLLogger.log("[SDLQUICClient] new state: \(state)", for: .debug) SDLLogger.log("[SDLQUICClient] new state: \(state)", for: .debug)
switch state { switch state {
case .ready: case .ready:
await self.readyState.markReady() self.markReady()
case .failed(let error): case .failed(let error):
await self.readyState.markFailed(error) self.markFailed(error)
self.emitEvent(.failed(error)) self.emitEvent(.failed(error))
case .cancelled: case .cancelled:
await self.readyState.markCancelled() self.markCancelled()
self.emitEvent(.cancelled) self.emitEvent(.cancelled)
case .setup, .preparing: case .setup, .preparing:
await self.readyState.markConnecting() self.markConnecting()
default: default:
() ()
} }
} }
func waitReady(timeout: Duration = .seconds(5)) async throws { func waitReady(timeout: Duration = .seconds(5)) async throws {
try await self.readyState.waitReady(timeout: timeout) try await self.waitReadyUntilStateChanged(timeout: timeout)
} }
func run() async -> SDLQUICClientExit { func run() async -> SDLQUICClientExit {
@ -183,7 +194,7 @@ actor SDLQUICClient {
func stop() async { func stop() async {
self.connection.cancel() self.connection.cancel()
await self.readyState.markCancelled() self.markCancelled()
self.finishStreams() self.finishStreams()
} }
@ -210,110 +221,100 @@ actor SDLQUICClient {
// --MARK: Ready // --MARK: Ready
extension SDLQUICClient { extension SDLQUICClient {
actor SDLQUICReadyState { private func waitReadyUntilStateChanged(timeout: Duration) async throws {
enum State { try Task.checkCancellation()
case idle
case connecting
case ready
case failed(Error)
case cancelled
}
private var state: State = .idle
private var continuations: [UUID: CheckedContinuation<Void, Error>] = [:]
func waitReady(timeout: Duration) async throws {
let id = UUID()
let timeoutTask = Task {
try? await Task.sleep(for: timeout)
if Task.isCancelled {
return
}
self.cancelWaiter(id: id, throwing: SDLQUICError.timeout)
}
defer {
timeoutTask.cancel()
}
try await withTaskCancellationHandler { try await withTaskCancellationHandler {
try await withCheckedThrowingContinuation { continuation in try await withCheckedThrowingContinuation { continuation in
self.addWaiter(id: id, continuation: continuation) switch self.readyStatus {
}
} onCancel: {
timeoutTask.cancel()
Task {
await self.cancelWaiter(id: id, throwing: CancellationError())
}
}
}
private func addWaiter(id: UUID, continuation: CheckedContinuation<Void, Error>) {
switch state {
case .ready: case .ready:
continuation.resume() continuation.resume()
case .failed(let error): case .failed(let error):
continuation.resume(throwing: error) continuation.resume(throwing: error)
case .cancelled: case .cancelled:
continuation.resume(throwing: CancellationError()) continuation.resume(throwing: SDLQUICError.connectionCancelled)
case .idle, .connecting: case .idle, .connecting:
continuations[id] = continuation guard self.readyContinuation == nil else {
continuation.resume(throwing: SDLQUICError.waitReadyAlreadyInProgress)
return
}
self.readyContinuation = continuation
self.readyTimeoutTask?.cancel()
self.readyTimeoutTask = Task {
do {
try await Task.sleep(for: timeout)
if !Task.isCancelled {
await self.cancelReadyWaiter(throwing: SDLQUICError.timeout)
}
} catch {
return
}
}
}
}
} onCancel: {
Task {
await self.cancelReadyWaiter(throwing: CancellationError())
}
} }
} }
private func cancelWaiter(id: UUID, throwing error: Error) { private func cancelReadyWaiter(throwing error: Error) {
guard let continuation = continuations.removeValue(forKey: id) else { guard let continuation = self.readyContinuation else {
return return
} }
self.readyContinuation = nil
self.readyTimeoutTask?.cancel()
self.readyTimeoutTask = nil
continuation.resume(throwing: error) continuation.resume(throwing: error)
} }
func markConnecting() { private func markConnecting() {
switch state { switch self.readyStatus {
case .idle: case .idle:
state = .connecting self.readyStatus = .connecting
default: default:
break break
} }
} }
func markReady() { private func markReady() {
state = .ready self.readyStatus = .ready
self.resumeReadyWaiter()
}
let list = continuations private func markFailed(_ error: Error) {
continuations.removeAll() self.readyStatus = .failed(error)
self.resumeReadyWaiter(throwing: error)
}
for continuation in list.values { private func markCancelled() {
self.readyStatus = .cancelled
self.resumeReadyWaiter(throwing: SDLQUICError.connectionCancelled)
}
private func resumeReadyWaiter() {
guard let continuation = self.readyContinuation else {
return
}
self.readyContinuation = nil
self.readyTimeoutTask?.cancel()
self.readyTimeoutTask = nil
continuation.resume() continuation.resume()
} }
private func resumeReadyWaiter(throwing error: Error) {
guard let continuation = self.readyContinuation else {
return
} }
func markFailed(_ error: Error) { self.readyContinuation = nil
state = .failed(error) self.readyTimeoutTask?.cancel()
self.readyTimeoutTask = nil
let list = continuations
continuations.removeAll()
for continuation in list.values {
continuation.resume(throwing: error) continuation.resume(throwing: error)
} }
}
func markCancelled() {
state = .cancelled
let list = continuations
continuations.removeAll()
for continuation in list.values {
continuation.resume(throwing: CancellationError())
}
}
}
} }
// --MARK: Reader // --MARK: Reader