This commit is contained in:
anlicheng 2026-05-28 17:15:04 +08:00
parent 47e45bbec4
commit 3217614f02
4 changed files with 74 additions and 14 deletions

View File

@ -62,6 +62,8 @@ actor SDLContextActor {
//
private let policyService: PolicyService
private var rootTask: Task<Void, Error>?
private var rootTaskID: UUID?
private var terminalError: Error?
private let readySignal = AsyncOneShot<Void>()
public init(provider: NEPacketTunnelProvider, config: SDLConfiguration, rsaCipher: RSACipher) {
@ -140,21 +142,31 @@ actor SDLContextActor {
return
}
let rootTaskID = UUID()
let rootTask = Task {
var result: Result<Void, Error> = .success(())
do {
try await self.runRootBody()
await self.cleanupRoot()
} catch is CancellationError {
SDLLogger.fatal("[SDLContext] root task cancelled", category: .context)
await self.cleanupRoot()
throw CancellationError()
if let terminalError = await self.consumeTerminalError() {
SDLLogger.fatal("[SDLContext] root task stopped by terminal error: \(terminalError)", category: .context)
result = .failure(terminalError)
} else {
SDLLogger.fatal("[SDLContext] root task cancelled", category: .context)
result = .failure(CancellationError())
}
} catch {
SDLLogger.fatal("[SDLContext] root task failed: \(error)", category: .context)
await self.readySignal.fail(error)
await self.cleanupRoot()
throw error
result = .failure(error)
}
await self.cleanupRoot()
await self.finishRootTask(id: rootTaskID)
try result.get()
}
self.rootTaskID = rootTaskID
self.rootTask = rootTask
do {
@ -164,6 +176,8 @@ actor SDLContextActor {
rootTask.cancel()
_ = try? await rootTask.value
self.rootTask = nil
self.rootTaskID = nil
self.terminalError = nil
throw error
}
}
@ -172,13 +186,14 @@ actor SDLContextActor {
public func stop() async {
SDLLogger.fatal("[SDLContext] stop requested", category: .context)
let rootTask = self.rootTask
self.rootTask = nil
rootTask?.cancel()
await self.readySignal.fail(CancellationError())
_ = try? await rootTask?.value
await self.cleanupRoot()
self.rootTask = nil
self.rootTaskID = nil
self.terminalError = nil
}
private func runRootBody() async throws {
@ -370,6 +385,28 @@ actor SDLContextActor {
await self.stopCurrentIPv6AssistClient()
}
private func requestRootShutdown(error: Error) async {
self.terminalError = error
await self.readySignal.fail(error)
self.rootTask?.cancel()
}
private func consumeTerminalError() -> Error? {
let error = self.terminalError
self.terminalError = nil
return error
}
private func finishRootTask(id: UUID) {
guard self.rootTaskID == id else {
return
}
self.rootTask = nil
self.rootTaskID = nil
self.terminalError = nil
}
deinit {
SDLLogger.log("[SDLContext] deinit", category: .context)
}
@ -441,7 +478,7 @@ extension SDLContextActor {
SDLLogger.fatal("[SDLContext] Super shutdown received: \(message)", category: .context)
self.publishTunnelEvent(message: message)
let error = NSError(domain: "com.jihe.punchnet.tun", code: -2)
self.provider.cancelTunnelWithError(error)
await self.failTunnel(error)
case .applyPolicyResponse(let policyResponse):
await self.policyService.applyPolicyResponse(policyResponse)
case .applyExposedServiceResponse(let response):
@ -469,8 +506,8 @@ extension SDLContextActor {
private func failTunnel(_ error: Error) async {
SDLLogger.fatal("[SDLContext] failTunnel: \(error)", category: .context)
await self.readySignal.fail(error)
self.provider.cancelTunnelWithError(error)
await self.requestRootShutdown(error: error)
}
private func requestExposedService() async {

View File

@ -82,8 +82,15 @@ actor DNSCloudClient {
}
/// DNS TUN IP
func forward(ipPacketData: Data) {
guard connection.state == .ready else {
func forward(ipPacketData: Data) async {
do {
try await self.readySignal.wait(timeout: .seconds(3))
} catch {
SDLLogger.log("[DNSCloudClient] drop query before ready: \(error)", category: .dns)
return
}
guard !self.isStopped, connection.state == .ready else {
return
}

View File

@ -104,8 +104,19 @@ actor DNSLocalClient {
}
}
func query(tracker: DNSTracker, dnsPayload: Data) {
guard connection.state == .ready, dnsPayload.count >= 2 else {
func query(tracker: DNSTracker, dnsPayload: Data) async {
guard dnsPayload.count >= 2 else {
return
}
do {
try await self.readySignal.wait(timeout: .seconds(3))
} catch {
SDLLogger.log("[DNSLocalClient] drop query before ready: \(error)", category: .dns)
return
}
guard !self.isStopped, connection.state == .ready else {
return
}
@ -146,6 +157,10 @@ actor DNSLocalClient {
private func handleConnectionStateUpdate(_ state: NWConnection.State) async {
switch state {
case .ready:
guard !self.isStopped else {
return
}
await self.readySignal.succeed(())
case .failed(let error):
await self.readySignal.fail(DNSLocalError.failed(error))

View File

@ -44,6 +44,7 @@ struct FlowSession: Hashable {
// MARK: -
final class FlowSessionTable: @unchecked Sendable {
private enum SessionState: Hashable {
case tcpPending
case tcpEstablished