Skip to content

Commit 1d199c3

Browse files
gmittertaciidgh
authored andcommitted
Implement SignalHandler on Windows
1 parent 7b0d17b commit 1d199c3

File tree

1 file changed

+42
-1
lines changed

1 file changed

+42
-1
lines changed

Sources/SPMUtility/InterruptHandler.swift

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,54 @@ import Basic
1414
/// Interrupt signal handling global variables
1515
private var wasInterrupted = false
1616
private var wasInterruptedLock = Lock()
17+
#if os(Windows)
18+
private var signalWatchingPipe: [HANDLE] = [INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE]
19+
#else
1720
private var signalWatchingPipe: [Int32] = [0, 0]
1821
private var oldAction = sigaction()
22+
#endif
1923

2024
/// This class can be used by command line tools to install a handler which
2125
/// should be called when a interrupt signal is delivered to the process.
2226
public final class InterruptHandler {
2327

2428
/// The thread which waits to be notified when a signal is received.
2529
let thread: Thread
30+
#if os(Windows)
31+
let signalHandler: @convention(c)(UInt32) -> Int32
32+
#else
33+
let signalHandler: @convention(c)(Int32) -> Void
34+
#endif
2635

2736
/// Start watching for interrupt signal and call the handler whenever the signal is received.
2837
public init(_ handler: @escaping () -> Void) throws {
2938
// Create a signal handler.
30-
let signalHandler: @convention(c)(Int32) -> Void = { _ in
39+
signalHandler = { _ in
3140
// Turn on the interrupt bool.
3241
wasInterruptedLock.withLock {
3342
wasInterrupted = true
3443
}
3544
// Write on pipe to notify the watching thread.
3645
var byte: UInt8 = 0
46+
#if os(Windows)
47+
var bytesWritten: DWORD = 0
48+
WriteFile(signalWatchingPipe[1], &byte, 1, &bytesWritten, nil)
49+
return TRUE
50+
#else
3751
write(signalWatchingPipe[1], &byte, 1)
52+
#endif
3853
}
54+
#if os(Windows)
55+
SetConsoleCtrlHandler(signalHandler, TRUE)
56+
57+
var readPipe: HANDLE?
58+
var writePipe: HANDLE?
59+
let rv = CreatePipe(&readPipe, &writePipe, nil, 1)
60+
signalWatchingPipe = [readPipe!, writePipe!]
61+
guard rv != FALSE else {
62+
throw SystemError.pipe(rv)
63+
}
64+
#else
3965
var action = sigaction()
4066
#if canImport(Darwin)
4167
action.__sigaction_u.__sa_handler = signalHandler
@@ -51,13 +77,19 @@ public final class InterruptHandler {
5177
guard rv == 0 else {
5278
throw SystemError.pipe(rv)
5379
}
80+
#endif
5481

5582
// This thread waits to be notified via pipe. If something is read from pipe, check the interrupt bool
5683
// and send termination signal to all spawned processes in the process group.
5784
thread = Thread {
5885
while true {
5986
var buf: Int8 = 0
87+
#if os(Windows)
88+
var n: DWORD = 0
89+
ReadFile(signalWatchingPipe[1], &buf, 1, &n, nil)
90+
#else
6091
let n = read(signalWatchingPipe[0], &buf, 1)
92+
#endif
6193
// Pipe closed, nothing to do.
6294
if n == 0 { break }
6395
// Read the value of wasInterrupted and set it to false.
@@ -71,15 +103,24 @@ public final class InterruptHandler {
71103
handler()
72104
}
73105
}
106+
#if os(Windows)
107+
CloseHandle(signalWatchingPipe[0])
108+
#else
74109
close(signalWatchingPipe[0])
110+
#endif
75111
}
76112
thread.start()
77113
}
78114

79115
deinit {
116+
#if os(Windows)
117+
SetConsoleCtrlHandler(signalHandler, FALSE)
118+
CloseHandle(signalWatchingPipe[1])
119+
#else
80120
// Restore the old action and close the write end of pipe.
81121
sigaction(SIGINT, &oldAction, nil)
82122
close(signalWatchingPipe[1])
123+
#endif
83124
thread.join()
84125
}
85126
}

0 commit comments

Comments
 (0)