From e7f58dce03748faa8a875d891b1599e1d1e151b0 Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Wed, 27 May 2026 19:47:44 +0800 Subject: [PATCH] fix superClient --- Tun/Concurrency/AsyncPromise.swift | 53 +++++++++++++++ Tun/Super/SDLSuperClient.swift | 104 ++++++++++++----------------- 2 files changed, 97 insertions(+), 60 deletions(-) create mode 100644 Tun/Concurrency/AsyncPromise.swift diff --git a/Tun/Concurrency/AsyncPromise.swift b/Tun/Concurrency/AsyncPromise.swift new file mode 100644 index 0000000..fa10515 --- /dev/null +++ b/Tun/Concurrency/AsyncPromise.swift @@ -0,0 +1,53 @@ +// +// AsyncPromise.swift +// punchnet +// +// Created by 安礼成 on 2026/5/27. +// +import Foundation + +public actor AsyncPromise { + private enum State { + case pending([CheckedContinuation]) + case completed(Result) + } + + private var state: State = .pending([]) + + public init() { + + } + + public func value() async throws -> Value { + try await withCheckedThrowingContinuation { continuation in + switch state { + case .pending(var continuations): + continuations.append(continuation) + state = .pending(continuations) + + case .completed(let result): + continuation.resume(with: result) + } + } + } + + public func succeed(_ value: Value) { + complete(.success(value)) + } + + public func fail(_ error: Error) { + complete(.failure(error)) + } + + private func complete(_ result: Result) { + guard case .pending(let continuations) = state else { + return + } + + state = .completed(result) + + for continuation in continuations { + continuation.resume(with: result) + } + } +} diff --git a/Tun/Super/SDLSuperClient.swift b/Tun/Super/SDLSuperClient.swift index e3e97fb..f4b53f2 100644 --- a/Tun/Super/SDLSuperClient.swift +++ b/Tun/Super/SDLSuperClient.swift @@ -13,6 +13,7 @@ final class SDLSuperClient: @unchecked Sendable { // 数据流 public let messageStream: AsyncThrowingStream private let messageCont: AsyncThrowingStream.Continuation + private let stateLock = NSLock() private var isStarted = false private var isStopped = false @@ -60,17 +61,43 @@ final class SDLSuperClient: @unchecked Sendable { guard self.markStarted() else { return } - - let stateStream = Self.makeStateStream(for: self.connection) + defer { self.stop() } + let stateStream = Self.makeStateStream(for: self.connection) + let promise = AsyncPromise() + self.connection.start(queue: self.queue) + try await withTaskCancellationHandler { - self.connection.start(queue: self.queue) - try await self.runStateLoop(stateStream) + try await withThrowingTaskGroup { group in + group.addTask { + do { + for try await state in stateStream { + switch state { + case .ready: + await promise.succeed(true) + default: + () + } + } + } catch let err { + await promise.fail(err) + throw err + } + } + + group.addTask { + _ = try await promise.value() + try Task.checkCancellation() + try await self.readLoop() + } + + try await group.next() + } } onCancel: { - self.connection.cancel() + self.stop() } } @@ -90,79 +117,36 @@ final class SDLSuperClient: @unchecked Sendable { return AsyncThrowingStream(bufferingPolicy: .bufferingNewest(16)) { continuation in connection.stateUpdateHandler = { state in SDLLogger.log("[SDLSuperClient] new state: \(state)", for: .debug) - continuation.yield(state) - switch state { case .failed(let error): - continuation.finish(throwing: SDLSuperError.connectionFailed(error)) + continuation.finish(throwing: error) case .cancelled: continuation.finish(throwing: SDLSuperError.connectionCancelled) default: - break + continuation.yield(state) } } - + continuation.onTermination = { _ in connection.stateUpdateHandler = nil } } } - private func runStateLoop(_ stateStream: AsyncThrowingStream) async throws { - do { - try await self.waitUntilReady(stateStream) - self.connection.stateUpdateHandler = nil - try await self.readLoop() - } catch is CancellationError { - throw CancellationError() - } catch let error as SDLSuperError { - self.finishMessageStream(throwing: error) - throw error - } catch { - let wrappedError = SDLSuperError.internalError(error) - self.finishMessageStream(throwing: wrappedError) - throw wrappedError - } - } - - private func waitUntilReady(_ stateStream: AsyncThrowingStream) async throws { - for try await state in stateStream { - try Task.checkCancellation() - - if case .ready = state { - return - } - } - - throw SDLSuperError.connectionCancelled - } - private func readLoop() async throws { let frameParser = SDLSuperFrameParser(maxBufferSize: self.maxBufferSize) - do { - while true { + while true { + try Task.checkCancellation() + let data = try await Self.readOnce(connection: self.connection) + let frames = try frameParser.parseFrames(data: data) + for frame in frames { try Task.checkCancellation() - let data = try await Self.readOnce(connection: self.connection) - let frames = try frameParser.parseFrames(data: data) - for frame in frames { - try Task.checkCancellation() - if let message = SDLSuperCodec.decode(frame: frame) { - self.messageCont.yield(message) - } else { - throw SDLSuperError.decodeError("invalid message") - } + if let message = SDLSuperCodec.decode(frame: frame) { + self.messageCont.yield(message) + } else { + throw SDLSuperError.decodeError("invalid message") } } - } catch is CancellationError { - throw CancellationError() - } catch let error as SDLSuperError { - self.finishMessageStream(throwing: error) - throw error - } catch { - let wrappedError = SDLSuperError.internalError(error) - self.finishMessageStream(throwing: wrappedError) - - throw wrappedError } }