@@ -2,46 +2,70 @@ import Foundation
22import Dispatch
33import SwiftMCP
44
5- // Keep a reference to the signal source so it isn't deallocated
6- fileprivate var sigintSource : DispatchSourceSignal ?
7- fileprivate var isShuttingDown = false
8-
9- /// Sets up a modern Swift signal handler for SIGINT.
10- func setupSignalHandler( transport: HTTPSSETransport ) {
11- // Create a dedicated dispatch queue for signal handling.
12- let signalQueue = DispatchQueue ( label: " com.cocoanetics.signalQueue " )
13- // Create a dispatch source on that queue.
14- sigintSource = DispatchSource . makeSignalSource ( signal: SIGINT, queue: signalQueue)
15-
16- // Tell the system to ignore the default SIGINT handler.
17- signal ( SIGINT, SIG_IGN)
18-
19- // Specify what to do when the signal is received.
20- sigintSource? . setEventHandler {
21- // Prevent multiple shutdown attempts
22- guard !isShuttingDown else { return }
23- isShuttingDown = true
5+ /// Handles SIGINT signals for graceful shutdown of the HTTP SSE transport
6+ public final class SignalHandler {
7+ /// Actor to manage signal handling state in a thread-safe way
8+ private actor State {
9+ private var sigintSource : DispatchSourceSignal ?
10+ private var isShuttingDown = false
11+ private weak var transport : HTTPSSETransport ?
2412
25- print ( " \n Shutting down... " )
26-
27- // Create a semaphore to wait for shutdown
28- let semaphore = DispatchSemaphore ( value: 0 )
13+ init ( transport: HTTPSSETransport ) {
14+ self . transport = transport
15+ }
2916
30- Task {
17+ func setupHandler( on queue: DispatchQueue ) {
18+ // Create a dispatch source on the provided queue
19+ sigintSource = DispatchSource . makeSignalSource ( signal: SIGINT, queue: queue)
20+
21+ // Tell the system to ignore the default SIGINT handler
22+ signal ( SIGINT, SIG_IGN)
23+
24+ // Specify what to do when the signal is received
25+ sigintSource? . setEventHandler { [ weak self] in
26+ Task { [ weak self] in
27+ await self ? . handleSignal ( )
28+ }
29+ }
30+
31+ // Start listening for the signal
32+ sigintSource? . resume ( )
33+ }
34+
35+ private func handleSignal( ) async {
36+ // Prevent multiple shutdown attempts
37+ guard !isShuttingDown else { return }
38+ isShuttingDown = true
39+
40+ print ( " \n Shutting down... " )
41+
42+ guard let transport = transport else {
43+ print ( " Transport no longer available " )
44+ Foundation . exit ( 1 )
45+ }
46+
3147 do {
3248 try await transport. stop ( )
33- semaphore . signal ( )
49+ Foundation . exit ( 0 )
3450 } catch {
3551 print ( " Error during shutdown: \( error) " )
36- semaphore . signal ( )
52+ Foundation . exit ( 1 )
3753 }
3854 }
39-
40- // Wait for shutdown to complete with timeout
41- _ = semaphore. wait ( timeout: . now( ) + . seconds( 5 ) )
42- Foundation . exit ( 0 )
4355 }
44-
45- // Start listening for the signal.
46- sigintSource? . resume ( )
56+
57+ // Instance state
58+ private let state : State
59+
60+ /// Creates a new signal handler for the given transport
61+ public init ( transport: HTTPSSETransport ) {
62+ self . state = State ( transport: transport)
63+ }
64+
65+ /// Sets up the SIGINT handler
66+ public func setup( ) async {
67+ // Create a dedicated dispatch queue for signal handling
68+ let signalQueue = DispatchQueue ( label: " com.cocoanetics.signalQueue " )
69+ await state. setupHandler ( on: signalQueue)
70+ }
4771}
0 commit comments