Files
steipete--codexbar/Sources/CodexBarCLI/CLITerminationSignalMonitor.swift
2026-07-13 12:22:33 +08:00

80 lines
2.3 KiB
Swift

import CodexBarCore
import Dispatch
import Foundation
#if canImport(Darwin)
import Darwin
#elseif canImport(Glibc)
import Glibc
#elseif canImport(Musl)
import Musl
#endif
private func handleCLITerminationSignal(_: Int32) {}
final class CLITerminationSignalMonitor: @unchecked Sendable {
static let signalNumbers = [SIGINT, SIGTERM, SIGHUP]
private let lock = NSLock()
private let sources: [DispatchSourceSignal]
private var isCancelled = false
init(onSignal: @escaping @Sendable (Int32) -> Void) {
self.sources = Self.signalNumbers.map { signalNumber in
Self.installCaptureHandler(for: signalNumber)
let source = DispatchSource.makeSignalSource(signal: signalNumber, queue: .global(qos: .utility))
source.setEventHandler {
onSignal(signalNumber)
}
source.resume()
return source
}
}
func cancel() {
self.lock.lock()
guard !self.isCancelled else {
self.lock.unlock()
return
}
self.isCancelled = true
self.lock.unlock()
for source in self.sources {
source.cancel()
}
for signalNumber in Self.signalNumbers {
Self.restoreDefaultHandler(for: signalNumber)
}
}
deinit {
self.cancel()
}
static func terminateActiveHelpersAndReraise(_ signalNumber: Int32) {
TTYCommandRunner.terminateActiveProcessesForAppShutdown()
self.restoreDefaultHandler(for: signalNumber)
_ = kill(getpid(), signalNumber)
}
private static func installCaptureHandler(for signalNumber: Int32) {
#if canImport(Darwin)
_ = Darwin.signal(signalNumber, handleCLITerminationSignal)
#elseif canImport(Glibc)
_ = Glibc.signal(signalNumber, handleCLITerminationSignal)
#elseif canImport(Musl)
_ = Musl.signal(signalNumber, handleCLITerminationSignal)
#endif
}
private static func restoreDefaultHandler(for signalNumber: Int32) {
#if canImport(Darwin)
_ = Darwin.signal(signalNumber, SIG_DFL)
#elseif canImport(Glibc)
_ = Glibc.signal(signalNumber, SIG_DFL)
#elseif canImport(Musl)
_ = Musl.signal(signalNumber, SIG_DFL)
#endif
}
}