import Foundation enum SDLRuntimeEnvironmentCommand { case start(completion: @Sendable (Error?) -> Void) case stop(completion: @Sendable () -> Void) } actor 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 private let commandCont: AsyncStream.Continuation private var commandTask: Task? 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 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() } } func shutdown() { self.commandCont.finish() self.commandTask?.cancel() self.commandTask = nil } deinit { self.commandCont.finish() self.commandTask?.cancel() } }