fix context

This commit is contained in:
anlicheng 2026-05-28 12:30:46 +08:00
parent bba1180b11
commit 43a19c030c
3 changed files with 151 additions and 147 deletions

View File

@ -14,36 +14,32 @@ enum TunnelError: Error {
} }
class PacketTunnelProvider: NEPacketTunnelProvider { class PacketTunnelProvider: NEPacketTunnelProvider {
private var runtimeEnv: SDLRuntimeEnvironment? 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)?
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) { override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
//
guard self.runtimeEnv == nil else {
completionHandler(TunnelError.invalidContext)
return
}
guard let options, let config = SDLConfiguration.parse(options: options) else { guard let options, let config = SDLConfiguration.parse(options: options) else {
completionHandler(TunnelError.invalidConfiguration) completionHandler(TunnelError.invalidConfiguration)
return return
} }
if self.runtimeEnv == nil { let rsaCipher = try! CCRSACipher(keySize: 1024)
let rsaCipher = try! CCRSACipher(keySize: 1024) self.startContext(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler)
self.runtimeEnv = SDLRuntimeEnvironment(config: config, rsaCipher: rsaCipher, provider: self)
self.runtimeEnv?.run()
}
self.runtimeEnv?.submitCommand(command: .start(completion: { err in
completionHandler(err)
}))
} }
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
// Add code here to start the process of stopping the tunnel. self.stopContext(clearRuntimeConfiguration: true, completionHandler: completionHandler)
self.runtimeEnv?.submitCommand(command: .stop(completion: {
completionHandler()
}))
} }
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
@ -65,29 +61,25 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
} }
override func sleep(completionHandler: @escaping () -> Void) { override func sleep(completionHandler: @escaping () -> Void) {
// Add code here to get ready to sleep. self.stopContext(clearRuntimeConfiguration: false) {
guard let runtimeEnv else {
completionHandler()
return
}
runtimeEnv.submitCommand(command: .stop(completion: {
SDLLogger.log("[PacketTunnelProvider] sleep") SDLLogger.log("[PacketTunnelProvider] sleep")
completionHandler() completionHandler()
})) }
} }
override func wake() { override func wake() {
SDLLogger.log("[PacketTunnelProvider] wake up!!!!!!!") SDLLogger.log("[PacketTunnelProvider] wake up!!!!!!!")
// Add code here to wake up. self.startCachedContext { err in
// if let err {
self.runtimeEnv?.submitCommand(command: .start(completion: { err in SDLLogger.log("[PacketTunnelProvider] wakeup start failed: \(err.localizedDescription)")
SDLLogger.log("[PacketTunnelProvider] wakeup and try start") } else {
})) SDLLogger.log("[PacketTunnelProvider] wakeup and try start")
}
}
} }
private func handleAppRequest(message: AppRequest) async throws -> Data? { private func handleAppRequest(message: AppRequest) async throws -> Data? {
guard let contextActor = self.runtimeEnv?.getContextActor() else { guard let contextActor = self.currentContextActor() else {
throw TunnelError.invalidContext throw TunnelError.invalidContext
} }
@ -117,3 +109,126 @@ 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 {
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 {
self.finishContextStart(contextActor, error: error)
}
}
case .starting, .running, .stopping:
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 {
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()
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()
}
}

View File

@ -1,112 +0,0 @@
import Foundation
enum SDLRuntimeEnvironmentCommand {
case start(completion: @Sendable (Error?) -> Void)
case stop(completion: @Sendable () -> Void)
}
final class SDLRuntimeEnvironment {
private enum State {
case idle
case running
}
private var state: State = .idle
private var contextActor: SDLContextActor?
private var config: SDLConfiguration
private let rsaCipher: CCRSACipher
private let provider: PacketTunnelProvider
private let commandStream: AsyncStream<SDLRuntimeEnvironmentCommand>
private let commandCont: AsyncStream<SDLRuntimeEnvironmentCommand>.Continuation
private var commandTask: Task<Void, Never>?
init(config: SDLConfiguration, rsaCipher: CCRSACipher, provider: PacketTunnelProvider) {
self.config = config
self.rsaCipher = rsaCipher
self.provider = provider
let pair = AsyncStream.makeStream(of: SDLRuntimeEnvironmentCommand.self)
self.commandStream = pair.stream
self.commandCont = pair.continuation
}
func submitCommand(command: SDLRuntimeEnvironmentCommand) {
self.commandCont.yield(command)
}
func getContextActor() -> SDLContextActor? {
self.contextActor
}
func run() {
let stream = self.commandStream
self.commandTask = Task { [weak self] in
for await command in stream {
guard let self else {
break
}
await self.handle(command)
}
}
}
private func handle(_ command: SDLRuntimeEnvironmentCommand) async {
switch command {
case .start(let handler):
do {
try await self.startCommand()
handler(nil)
} catch {
handler(error)
}
case .stop(let handler):
await self.stopCommand()
handler()
}
}
private func startCommand() async throws {
switch self.state {
case .idle:
SDLTunnelAppNotifier.shared.clear()
let contextActor = SDLContextActor(
provider: provider,
config: config,
rsaCipher: self.rsaCipher
)
self.contextActor = contextActor
try await contextActor.start()
self.state = .running
case .running:
SDLLogger.log("[SDLRuntimeEnvironment] is running, ignore start command")
}
}
private func stopCommand() async {
switch self.state {
case .idle:
SDLLogger.log("[SDLRuntimeEnvironment] is idle, ignore stop command")
case .running:
let contextActor = self.contextActor
self.contextActor = nil
self.state = .idle
await contextActor?.stop()
}
}
deinit {
self.commandCont.finish()
self.commandTask?.cancel()
self.commandTask = nil
}
}

View File

@ -171,6 +171,7 @@ actor SDLContextActor {
self.rootTask = nil self.rootTask = nil
rootTask?.cancel() rootTask?.cancel()
await self.readySignal.fail(CancellationError())
_ = try? await rootTask?.value _ = try? await rootTask?.value
await self.cleanupRoot() await self.cleanupRoot()