fix Service

This commit is contained in:
anlicheng 2026-06-22 14:42:34 +08:00
parent 3e90e3788a
commit 5b990535fa
6 changed files with 344 additions and 146 deletions

View File

@ -287,27 +287,19 @@ actor SDLContextActor {
} }
group.addTask { group.addTask {
try await Self.runRestarting(name: "udpHoleService") { try await udpHoleService.run()
try await udpHoleService.run()
}
} }
group.addTask { group.addTask {
try await Self.runRestarting(name: "udpHoleV6Service") { try await udpHoleV6Service.run()
try await udpHoleV6Service.run()
}
} }
group.addTask { group.addTask {
try await Self.runRestarting(name: "dnsCloudService") { try await dnsCloudService.run()
try await dnsCloudService.run()
}
} }
group.addTask { group.addTask {
try await Self.runRestarting(name: "dnsLocalService") { try await dnsLocalService.run()
try await dnsLocalService.run()
}
} }
group.addTask { group.addTask {
@ -618,27 +610,6 @@ extension SDLContextActor {
extension SDLContextActor { extension SDLContextActor {
private static func runRestarting(
name: String,
retryDelay: Duration = .seconds(5),
operation: @escaping @Sendable () async throws -> Void
) async throws {
while !Task.isCancelled {
do {
try Task.checkCancellation()
try await operation()
SDLLogger.log("[SDLContext] worker \(name) ended, will restart", category: .context)
} catch is CancellationError {
SDLLogger.log("[SDLContext] worker \(name) cancelled", category: .context)
throw CancellationError()
} catch {
SDLLogger.log("[SDLContext] worker \(name) crashed: \(error.localizedDescription), will restart", category: .context)
}
try await Task.sleep(for: retryDelay)
}
}
private static func runPeriodic( private static func runPeriodic(
name: String, name: String,
interval: Duration, interval: Duration,

View File

@ -5,9 +5,14 @@ actor DNSCloudService {
private var onEvent: DNSEventHandler = { _ in } private var onEvent: DNSEventHandler = { _ in }
private var currentClient: DNSCloudClient? private var currentClient: DNSCloudClient?
private var isRunning = false
private var isStopping = false
private var needsImmediateRestart = false
private let retryDelay: Duration
init(serverIP: String) { init(serverIP: String, retryDelay: Duration = .seconds(5)) {
self.serverIP = serverIP self.serverIP = serverIP
self.retryDelay = retryDelay
} }
func updateEventHandler(_ onEvent: @escaping DNSEventHandler) { func updateEventHandler(_ onEvent: @escaping DNSEventHandler) {
@ -15,54 +20,69 @@ actor DNSCloudService {
} }
func run() async throws { func run() async throws {
let client = DNSCloudClient(serverIP: self.serverIP, port: 15353) guard !self.isRunning else {
self.currentClient = client return
defer {
self.clearCurrent(client)
} }
do { self.isRunning = true
let onEvent = self.onEvent self.isStopping = false
try await withThrowingTaskGroup(of: Void.self) { group in defer {
defer { self.isRunning = false
group.cancelAll() self.currentClient = nil
}
while !Task.isCancelled, !self.isStopping {
let client = DNSCloudClient(serverIP: self.serverIP, port: 15353)
self.currentClient = client
do {
try await self.run(client: client)
self.clearCurrent(client)
await client.stop()
guard !self.isStopping else {
break
} }
group.addTask { SDLLogger.log("[DNSCloudService] dnsCloudClient ended, will restart", category: .dns)
try await client.run() } catch is CancellationError {
self.clearCurrent(client)
await client.stop()
throw CancellationError()
} catch {
self.clearCurrent(client)
await client.stop()
guard !self.isStopping else {
break
} }
group.addTask { SDLLogger.log("[DNSCloudService] dnsCloudClient failed: \(error.localizedDescription), will restart", category: .dns)
for try await packet in client.packetFlow {
try Task.checkCancellation()
await onEvent(.packet(packet))
}
}
_ = try await group.next()
} }
await client.stop() if self.consumeImmediateRestartRequest() {
} catch is CancellationError { SDLLogger.log("[DNSCloudService] dnsCloudClient invalidated after wakeup, will restart immediately", category: .dns)
await client.stop() continue
throw CancellationError() }
} catch {
await client.stop() try await Task.sleep(for: self.retryDelay)
throw error
} }
} }
func stop() async { func stop() async {
let client = self.currentClient self.isStopping = true
self.currentClient = nil self.needsImmediateRestart = false
await self.invalidateCurrentClient()
await client?.stop()
} }
func recoverAfterWake() async { func recoverAfterWake() async {
await self.stop() guard !self.isStopping else {
return
}
self.needsImmediateRestart = self.currentClient != nil
await self.invalidateCurrentClient()
} }
func forward(ipPacketData: Data) async { func forward(ipPacketData: Data) async {
@ -74,4 +94,40 @@ actor DNSCloudService {
self.currentClient = nil self.currentClient = nil
} }
} }
private func invalidateCurrentClient() async {
let client = self.currentClient
self.currentClient = nil
await client?.stop()
}
private func consumeImmediateRestartRequest() -> Bool {
let needsImmediateRestart = self.needsImmediateRestart
self.needsImmediateRestart = false
return needsImmediateRestart
}
private func run(client: DNSCloudClient) async throws {
let onEvent = self.onEvent
try await withThrowingTaskGroup(of: Void.self) { group in
defer {
group.cancelAll()
}
group.addTask {
try await client.run()
}
group.addTask {
for try await packet in client.packetFlow {
try Task.checkCancellation()
await onEvent(.packet(packet))
}
}
_ = try await group.next()
}
}
} }

View File

@ -5,9 +5,14 @@ actor DNSLocalService {
private var onEvent: DNSEventHandler = { _ in } private var onEvent: DNSEventHandler = { _ in }
private var currentClient: DNSLocalClient? private var currentClient: DNSLocalClient?
private var isRunning = false
private var isStopping = false
private var needsImmediateRestart = false
private let retryDelay: Duration
init(publicDnsServers: [String]) { init(publicDnsServers: [String], retryDelay: Duration = .seconds(5)) {
self.publicDnsServers = publicDnsServers self.publicDnsServers = publicDnsServers
self.retryDelay = retryDelay
} }
func updateEventHandler(_ onEvent: @escaping DNSEventHandler) { func updateEventHandler(_ onEvent: @escaping DNSEventHandler) {
@ -15,36 +20,72 @@ actor DNSLocalService {
} }
func run() async throws { func run() async throws {
let dnsServer = self.publicDnsServers.randomElement() ?? "223.5.5.5" guard !self.isRunning else {
let client = DNSLocalClient(host: dnsServer) return
self.currentClient = client }
SDLLogger.log("[DNSLocalService] dnsLocalClient started", category: .dns) self.isRunning = true
self.isStopping = false
do { defer {
try await self.run(client: client) self.isRunning = false
self.clearCurrent(client) self.currentClient = nil
await client.stop() }
} catch is CancellationError {
self.clearCurrent(client) while !Task.isCancelled, !self.isStopping {
await client.stop() let dnsServer = self.publicDnsServers.randomElement() ?? "223.5.5.5"
throw CancellationError() let client = DNSLocalClient(host: dnsServer)
} catch { self.currentClient = client
self.clearCurrent(client)
await client.stop() SDLLogger.log("[DNSLocalService] dnsLocalClient started", category: .dns)
throw error
do {
try await self.run(client: client)
self.clearCurrent(client)
await client.stop()
guard !self.isStopping else {
break
}
SDLLogger.log("[DNSLocalService] dnsLocalClient ended, will restart", category: .dns)
} catch is CancellationError {
self.clearCurrent(client)
await client.stop()
throw CancellationError()
} catch {
self.clearCurrent(client)
await client.stop()
guard !self.isStopping else {
break
}
SDLLogger.log("[DNSLocalService] dnsLocalClient failed: \(error.localizedDescription), will restart", category: .dns)
}
if self.consumeImmediateRestartRequest() {
SDLLogger.log("[DNSLocalService] dnsLocalClient invalidated after wakeup, will restart immediately", category: .dns)
continue
}
try await Task.sleep(for: self.retryDelay)
} }
} }
func stop() async { func stop() async {
let client = self.currentClient self.isStopping = true
self.currentClient = nil self.needsImmediateRestart = false
await self.invalidateCurrentClient()
await client?.stop()
} }
func recoverAfterWake() async { func recoverAfterWake() async {
await self.stop() guard !self.isStopping else {
return
}
self.needsImmediateRestart = self.currentClient != nil
await self.invalidateCurrentClient()
} }
func query(tracker: DNSLocalClient.DNSTracker, dnsPayload: Data) async { func query(tracker: DNSLocalClient.DNSTracker, dnsPayload: Data) async {
@ -79,4 +120,17 @@ actor DNSLocalService {
self.currentClient = nil self.currentClient = nil
} }
} }
private func invalidateCurrentClient() async {
let client = self.currentClient
self.currentClient = nil
await client?.stop()
}
private func consumeImmediateRestartRequest() -> Bool {
let needsImmediateRestart = self.needsImmediateRestart
self.needsImmediateRestart = false
return needsImmediateRestart
}
} }

View File

@ -53,6 +53,11 @@ actor SDLSuperService {
try await session.run() try await session.run()
self.clearCurrent(session, generation: generation) self.clearCurrent(session, generation: generation)
await session.stop() await session.stop()
guard !self.isStopping else {
break
}
SDLLogger.log("[SDLSuperService] session ended, will restart", category: .super) SDLLogger.log("[SDLSuperService] session ended, will restart", category: .super)
} catch is CancellationError { } catch is CancellationError {
self.clearCurrent(session, generation: generation) self.clearCurrent(session, generation: generation)
@ -80,6 +85,7 @@ actor SDLSuperService {
func stop() async { func stop() async {
self.isStopping = true self.isStopping = true
self.needsImmediateRestart = false
await self.invalidateCurrentSession() await self.invalidateCurrentSession()
} }
@ -88,7 +94,7 @@ actor SDLSuperService {
return return
} }
self.needsImmediateRestart = true self.needsImmediateRestart = self.currentSession != nil
await self.invalidateCurrentSession() await self.invalidateCurrentSession()
} }

View File

@ -18,9 +18,14 @@ actor SDLUDPHoleService {
private var onData: DataHandler = { _ in } private var onData: DataHandler = { _ in }
private var currentSession: SDLUDPHoleSession? private var currentSession: SDLUDPHoleSession?
private var generation: UInt64 = 0 private var generation: UInt64 = 0
private var isRunning = false
private var isStopping = false
private var needsImmediateRestart = false
private let retryDelay: Duration
init(proberActor: SDLNATProberActor) { init(proberActor: SDLNATProberActor, retryDelay: Duration = .seconds(5)) {
self.proberActor = proberActor self.proberActor = proberActor
self.retryDelay = retryDelay
} }
func updateHandlers(onEvent: @escaping EventHandler, onData: @escaping DataHandler) { func updateHandlers(onEvent: @escaping EventHandler, onData: @escaping DataHandler) {
@ -29,44 +34,77 @@ actor SDLUDPHoleService {
} }
func run() async throws { func run() async throws {
let generation = self.nextGeneration() guard !self.isRunning else {
let session = SDLUDPHoleSession( return
proberActor: self.proberActor, }
onEvent: { [weak self] event in
await self?.handleEvent(event, generation: generation)
},
onData: self.onData
)
self.currentSession = session self.isRunning = true
self.isStopping = false
do { defer {
try await session.run() self.isRunning = false
self.clearCurrent(session, generation: generation) self.currentSession = nil
await session.stop() }
} catch is CancellationError {
self.clearCurrent(session, generation: generation) while !Task.isCancelled, !self.isStopping {
await session.stop() let generation = self.nextGeneration()
throw CancellationError() let session = SDLUDPHoleSession(
} catch { proberActor: self.proberActor,
self.clearCurrent(session, generation: generation) onEvent: { [weak self] event in
await session.stop() await self?.handleEvent(event, generation: generation)
throw error },
onData: self.onData
)
self.currentSession = session
do {
try await session.run()
self.clearCurrent(session, generation: generation)
await session.stop()
guard !self.isStopping else {
break
}
SDLLogger.log("[SDLUDPHoleService] session ended, will restart", category: .udpHole)
} catch is CancellationError {
self.clearCurrent(session, generation: generation)
await session.stop()
throw CancellationError()
} catch {
self.clearCurrent(session, generation: generation)
await session.stop()
guard !self.isStopping else {
break
}
SDLLogger.log("[SDLUDPHoleService] session failed: \(error.localizedDescription), will restart", category: .udpHole)
}
if self.consumeImmediateRestartRequest() {
SDLLogger.log("[SDLUDPHoleService] session invalidated after wakeup, will restart immediately", category: .udpHole)
continue
}
try await Task.sleep(for: self.retryDelay)
} }
} }
func stop() async { func stop() async {
self.generation &+= 1 self.isStopping = true
self.needsImmediateRestart = false
let session = self.currentSession await self.invalidateCurrentSession()
self.currentSession = nil
await session?.stop()
await self.proberActor.cancelAll()
} }
func recoverAfterWake() async { func recoverAfterWake() async {
await self.stop() guard !self.isStopping else {
return
}
self.needsImmediateRestart = self.currentSession != nil
await self.invalidateCurrentSession()
} }
func send(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) async { func send(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) async {
@ -78,6 +116,22 @@ actor SDLUDPHoleService {
return self.generation return self.generation
} }
private func invalidateCurrentSession() async {
self.generation &+= 1
let session = self.currentSession
self.currentSession = nil
await session?.stop()
await self.proberActor.cancelAll()
}
private func consumeImmediateRestartRequest() -> Bool {
let needsImmediateRestart = self.needsImmediateRestart
self.needsImmediateRestart = false
return needsImmediateRestart
}
private func clearCurrent(_ session: SDLUDPHoleSession, generation: UInt64) { private func clearCurrent(_ session: SDLUDPHoleSession, generation: UInt64) {
guard self.generation == generation else { guard self.generation == generation else {
return return

View File

@ -9,6 +9,14 @@ actor SDLUDPHoleV6Service {
private var onData: DataHandler = { _ in } private var onData: DataHandler = { _ in }
private var currentSession: SDLUDPHoleV6Session? private var currentSession: SDLUDPHoleV6Session?
private var generation: UInt64 = 0 private var generation: UInt64 = 0
private var isRunning = false
private var isStopping = false
private var needsImmediateRestart = false
private let retryDelay: Duration
init(retryDelay: Duration = .seconds(5)) {
self.retryDelay = retryDelay
}
func updateHandlers(onEvent: @escaping EventHandler, onData: @escaping DataHandler) { func updateHandlers(onEvent: @escaping EventHandler, onData: @escaping DataHandler) {
self.onEvent = onEvent self.onEvent = onEvent
@ -16,42 +24,76 @@ actor SDLUDPHoleV6Service {
} }
func run() async throws { func run() async throws {
let generation = self.nextGeneration() guard !self.isRunning else {
let session = SDLUDPHoleV6Session( return
onEvent: { [weak self] event in }
await self?.handleEvent(event, generation: generation)
},
onData: self.onData
)
self.currentSession = session self.isRunning = true
self.isStopping = false
do { defer {
try await session.run() self.isRunning = false
self.clearCurrent(session, generation: generation) self.currentSession = nil
await session.stop() }
} catch is CancellationError {
self.clearCurrent(session, generation: generation) while !Task.isCancelled, !self.isStopping {
await session.stop() let generation = self.nextGeneration()
throw CancellationError() let session = SDLUDPHoleV6Session(
} catch { onEvent: { [weak self] event in
self.clearCurrent(session, generation: generation) await self?.handleEvent(event, generation: generation)
await session.stop() },
throw error onData: self.onData
)
self.currentSession = session
do {
try await session.run()
self.clearCurrent(session, generation: generation)
await session.stop()
guard !self.isStopping else {
break
}
SDLLogger.log("[SDLUDPHoleV6Service] session ended, will restart", category: .udpHole)
} catch is CancellationError {
self.clearCurrent(session, generation: generation)
await session.stop()
throw CancellationError()
} catch {
self.clearCurrent(session, generation: generation)
await session.stop()
guard !self.isStopping else {
break
}
SDLLogger.log("[SDLUDPHoleV6Service] session failed: \(error.localizedDescription), will restart", category: .udpHole)
}
if self.consumeImmediateRestartRequest() {
SDLLogger.log("[SDLUDPHoleV6Service] session invalidated after wakeup, will restart immediately", category: .udpHole)
continue
}
try await Task.sleep(for: self.retryDelay)
} }
} }
func stop() async { func stop() async {
self.generation &+= 1 self.isStopping = true
self.needsImmediateRestart = false
let session = self.currentSession await self.invalidateCurrentSession()
self.currentSession = nil
await session?.stop()
} }
func recoverAfterWake() async { func recoverAfterWake() async {
await self.stop() guard !self.isStopping else {
return
}
self.needsImmediateRestart = self.currentSession != nil
await self.invalidateCurrentSession()
} }
func send(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) async { func send(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) async {
@ -63,6 +105,21 @@ actor SDLUDPHoleV6Service {
return self.generation return self.generation
} }
private func invalidateCurrentSession() async {
self.generation &+= 1
let session = self.currentSession
self.currentSession = nil
await session?.stop()
}
private func consumeImmediateRestartRequest() -> Bool {
let needsImmediateRestart = self.needsImmediateRestart
self.needsImmediateRestart = false
return needsImmediateRestart
}
private func clearCurrent(_ session: SDLUDPHoleV6Session, generation: UInt64) { private func clearCurrent(_ session: SDLUDPHoleV6Session, generation: UInt64) {
guard self.generation == generation else { guard self.generation == generation else {
return return