fix SDLContext

This commit is contained in:
anlicheng 2026-05-28 23:39:12 +08:00
parent 362117aa84
commit 5813107425
2 changed files with 172 additions and 147 deletions

View File

@ -0,0 +1,160 @@
//
// SDLContextBootstrap.swift
// Tun
//
// Created by Codex on 2026/5/28.
//
import Foundation
final class SDLContextBootstrap: @unchecked Sendable {
private enum RuntimeState {
case idle
case starting
case running
case stopping
}
private weak var provider: PacketTunnelProvider?
private let runtimeLock = NSLock()
private var runtimeState: RuntimeState = .idle
private var config: SDLConfiguration?
private var rsaCipher: CCRSACipher?
private var contextActor: SDLContextActor?
private var startCompletionHandler: ((Error?) -> Void)?
init(provider: PacketTunnelProvider) {
self.provider = provider
}
func startCached(completionHandler: @escaping (Error?) -> Void) {
self.runtimeLock.lock()
let config = self.config
let rsaCipher = self.rsaCipher
self.runtimeLock.unlock()
guard let config, let rsaCipher else {
SDLLogger.fatal("[SDLContextBootstrap] startCached failed: missing cached runtime configuration", category: .app)
completionHandler(TunnelError.invalidConfiguration)
return
}
self.start(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler)
}
func start(config: SDLConfiguration, rsaCipher: CCRSACipher, completionHandler: @escaping (Error?) -> Void) {
guard let provider = self.provider else {
SDLLogger.fatal("[SDLContextBootstrap] start rejected: provider released", category: .app)
completionHandler(TunnelError.invalidContext)
return
}
self.runtimeLock.lock()
switch self.runtimeState {
case .idle:
SDLTunnelAppNotifier.shared.clear()
let contextActor = SDLContextActor(
provider: provider,
config: config,
rsaCipher: rsaCipher
)
self.config = config
self.rsaCipher = rsaCipher
self.contextActor = contextActor
self.startCompletionHandler = completionHandler
self.runtimeState = .starting
self.runtimeLock.unlock()
Task {
do {
try await contextActor.start()
self.finishContextStart(contextActor, error: nil)
} catch {
SDLLogger.fatal("[SDLContextBootstrap] context start failed: \(error)", category: .app)
self.finishContextStart(contextActor, error: error)
}
}
case .starting, .running, .stopping:
SDLLogger.fatal("[SDLContextBootstrap] start rejected: invalid runtime state \(self.runtimeState)", category: .app)
self.runtimeLock.unlock()
completionHandler(TunnelError.invalidContext)
}
}
func stop(clearRuntimeConfiguration: Bool, completionHandler: @escaping () -> Void) {
self.runtimeLock.lock()
let contextActor = self.contextActor
let startCompletionHandler = self.startCompletionHandler
guard let contextActor else {
SDLLogger.fatal("[SDLContextBootstrap] stop requested while context is nil, clearRuntimeConfiguration: \(clearRuntimeConfiguration)", category: .app)
self.runtimeState = .idle
self.startCompletionHandler = nil
if clearRuntimeConfiguration {
self.config = nil
self.rsaCipher = nil
}
self.runtimeLock.unlock()
startCompletionHandler?(TunnelError.invalidContext)
completionHandler()
return
}
self.contextActor = nil
self.startCompletionHandler = nil
self.runtimeState = .stopping
if clearRuntimeConfiguration {
self.config = nil
self.rsaCipher = nil
}
self.runtimeLock.unlock()
SDLLogger.fatal("[SDLContextBootstrap] stop will stop current context, clearRuntimeConfiguration: \(clearRuntimeConfiguration)", category: .app)
startCompletionHandler?(TunnelError.invalidContext)
Task {
await contextActor.stop()
self.markContextStopped()
completionHandler()
}
}
func currentContextActor() -> SDLContextActor? {
self.runtimeLock.lock()
let contextActor = self.contextActor
self.runtimeLock.unlock()
return contextActor
}
private func finishContextStart(_ contextActor: SDLContextActor, error: Error?) {
self.runtimeLock.lock()
guard self.contextActor === contextActor else {
self.runtimeLock.unlock()
return
}
let startCompletionHandler = self.startCompletionHandler
self.startCompletionHandler = nil
if let error {
self.contextActor = nil
self.runtimeState = .idle
} else {
self.runtimeState = .running
}
self.runtimeLock.unlock()
startCompletionHandler?(error)
}
private func markContextStopped() {
self.runtimeLock.lock()
if self.contextActor == nil {
self.runtimeState = .idle
}
self.runtimeLock.unlock()
}
}

View File

@ -14,19 +14,7 @@ enum TunnelError: Error {
}
class PacketTunnelProvider: NEPacketTunnelProvider {
private enum RuntimeState {
case idle
case starting
case running
case stopping
}
private let runtimeLock = NSLock()
private var runtimeState: RuntimeState = .idle
private var config: SDLConfiguration?
private var rsaCipher: CCRSACipher?
private var contextActor: SDLContextActor?
private var startCompletionHandler: ((Error?) -> Void)?
private lazy var contextBootstrap = SDLContextBootstrap(provider: self)
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
guard let options, let config = SDLConfiguration.parse(options: options) else {
@ -35,13 +23,18 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
return
}
let rsaCipher = try! CCRSACipher(keySize: 1024)
self.startContext(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler)
do {
let rsaCipher = try CCRSACipher(keySize: 1024)
self.contextBootstrap.start(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler)
} catch {
SDLLogger.fatal("[PacketTunnelProvider] startTunnel failed: rsa cipher initialization failed: \(error)", category: .app)
completionHandler(error)
}
}
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
SDLLogger.fatal("[PacketTunnelProvider] stopTunnel requested, reason: \(reason.rawValue)", category: .app)
self.stopContext(clearRuntimeConfiguration: true, completionHandler: completionHandler)
self.contextBootstrap.stop(clearRuntimeConfiguration: true, completionHandler: completionHandler)
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
@ -64,7 +57,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
override func sleep(completionHandler: @escaping () -> Void) {
SDLLogger.fatal("[PacketTunnelProvider] sleep requested, will stop current context", category: .app)
self.stopContext(clearRuntimeConfiguration: false) {
self.contextBootstrap.stop(clearRuntimeConfiguration: false) {
SDLLogger.log("[PacketTunnelProvider] sleep", category: .app)
completionHandler()
}
@ -72,7 +65,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
override func wake() {
SDLLogger.log("[PacketTunnelProvider] wake up!!!!!!!", category: .app)
self.startCachedContext { err in
self.contextBootstrap.startCached { err in
if let err {
SDLLogger.fatal("[PacketTunnelProvider] wakeup start failed: \(err)", category: .app)
SDLLogger.log("[PacketTunnelProvider] wakeup start failed: \(err.localizedDescription)", category: .app)
@ -83,7 +76,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
private func handleAppRequest(message: AppRequest) async throws -> Data? {
guard let contextActor = self.currentContextActor() else {
guard let contextActor = self.contextBootstrap.currentContextActor() else {
throw TunnelError.invalidContext
}
@ -113,131 +106,3 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
}
private extension PacketTunnelProvider {
func startCachedContext(completionHandler: @escaping (Error?) -> Void) {
self.runtimeLock.lock()
let config = self.config
let rsaCipher = self.rsaCipher
self.runtimeLock.unlock()
guard let config, let rsaCipher else {
SDLLogger.fatal("[PacketTunnelProvider] startCachedContext failed: missing cached runtime configuration", category: .app)
completionHandler(TunnelError.invalidConfiguration)
return
}
self.startContext(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler)
}
func startContext(config: SDLConfiguration, rsaCipher: CCRSACipher, completionHandler: @escaping (Error?) -> Void) {
self.runtimeLock.lock()
switch self.runtimeState {
case .idle:
SDLTunnelAppNotifier.shared.clear()
let contextActor = SDLContextActor(
provider: self,
config: config,
rsaCipher: rsaCipher
)
self.config = config
self.rsaCipher = rsaCipher
self.contextActor = contextActor
self.startCompletionHandler = completionHandler
self.runtimeState = .starting
self.runtimeLock.unlock()
Task {
do {
try await contextActor.start()
self.finishContextStart(contextActor, error: nil)
} catch {
SDLLogger.fatal("[PacketTunnelProvider] context start failed: \(error)", category: .app)
self.finishContextStart(contextActor, error: error)
}
}
case .starting, .running, .stopping:
SDLLogger.fatal("[PacketTunnelProvider] startContext rejected: invalid runtime state \(self.runtimeState)", category: .app)
self.runtimeLock.unlock()
completionHandler(TunnelError.invalidContext)
}
}
func stopContext(clearRuntimeConfiguration: Bool, completionHandler: @escaping () -> Void) {
self.runtimeLock.lock()
let contextActor = self.contextActor
let startCompletionHandler = self.startCompletionHandler
guard contextActor != nil else {
SDLLogger.fatal("[PacketTunnelProvider] stopContext requested while context is nil, clearRuntimeConfiguration: \(clearRuntimeConfiguration)", category: .app)
self.runtimeState = .idle
self.startCompletionHandler = nil
if clearRuntimeConfiguration {
self.config = nil
self.rsaCipher = nil
}
self.runtimeLock.unlock()
startCompletionHandler?(TunnelError.invalidContext)
completionHandler()
return
}
self.contextActor = nil
self.startCompletionHandler = nil
self.runtimeState = .stopping
if clearRuntimeConfiguration {
self.config = nil
self.rsaCipher = nil
}
self.runtimeLock.unlock()
SDLLogger.fatal("[PacketTunnelProvider] stopContext will stop current context, clearRuntimeConfiguration: \(clearRuntimeConfiguration)", category: .app)
startCompletionHandler?(TunnelError.invalidContext)
Task {
await contextActor?.stop()
self.markContextStopped()
completionHandler()
}
}
func currentContextActor() -> SDLContextActor? {
self.runtimeLock.lock()
let contextActor = self.contextActor
self.runtimeLock.unlock()
return contextActor
}
func finishContextStart(_ contextActor: SDLContextActor, error: Error?) {
self.runtimeLock.lock()
guard self.contextActor === contextActor else {
self.runtimeLock.unlock()
return
}
let startCompletionHandler = self.startCompletionHandler
self.startCompletionHandler = nil
if let error {
self.contextActor = nil
self.runtimeState = .idle
} else {
self.runtimeState = .running
}
self.runtimeLock.unlock()
startCompletionHandler?(error)
}
func markContextStopped() {
self.runtimeLock.lock()
if self.contextActor == nil {
self.runtimeState = .idle
}
self.runtimeLock.unlock()
}
}