Skip to content

Commit b6e28a9

Browse files
authored
fix InterruptHandler to be safer (swiftlang#249)
1 parent d0bca40 commit b6e28a9

File tree

1 file changed

+38
-31
lines changed

1 file changed

+38
-31
lines changed

Sources/TSCUtility/InterruptHandler.swift

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@
1111
import TSCLibc
1212
import TSCBasic
1313

14-
/// Interrupt signal handling global variables
15-
private var wasInterrupted = false
16-
private var wasInterruptedLock = Lock()
17-
#if os(Windows)
18-
private var signalWatchingPipe: [HANDLE] = [INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE]
19-
#else
20-
private var signalWatchingPipe: [Int32] = [0, 0]
21-
private var oldAction = sigaction()
22-
#endif
23-
2414
/// This class can be used by command line tools to install a handler which
2515
/// should be called when a interrupt signal is delivered to the process.
16+
@available(*, deprecated, message: "use DispatchSource instead")
2617
public final class InterruptHandler {
18+
/// Interrupt signal handling global variables
19+
private static var wasInterrupted = false
20+
private static var wasInterruptedLock = Lock()
21+
#if os(Windows)
22+
private static var signalWatchingPipe: [HANDLE] = [INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE]
23+
#else
24+
private static var signalWatchingPipe: [Int32] = [0, 0]
25+
private static var oldAction = sigaction()
26+
#endif
2727

2828
/// The thread which waits to be notified when a signal is received.
2929
let thread: Thread
@@ -35,20 +35,27 @@ public final class InterruptHandler {
3535

3636
/// Start watching for interrupt signal and call the handler whenever the signal is received.
3737
public init(_ handler: @escaping () -> Void) throws {
38+
// Swift reserves the right to lazily-initialize globals & statics
39+
// force initialize the statics so they as signal-safe
40+
_ = Self.wasInterrupted
41+
_ = Self.wasInterruptedLock
42+
_ = Self.signalWatchingPipe
43+
_ = Self.oldAction
44+
3845
// Create a signal handler.
39-
signalHandler = { _ in
46+
self.signalHandler = { _ in
4047
// Turn on the interrupt bool.
41-
wasInterruptedLock.withLock {
42-
wasInterrupted = true
48+
InterruptHandler.wasInterruptedLock.withLock {
49+
InterruptHandler.wasInterrupted = true
4350
}
4451
// Write on pipe to notify the watching thread.
4552
var byte: UInt8 = 0
4653
#if os(Windows)
4754
var bytesWritten: DWORD = 0
48-
WriteFile(signalWatchingPipe[1], &byte, 1, &bytesWritten, nil)
55+
WriteFile(InterruptHandler.signalWatchingPipe[1], &byte, 1, &bytesWritten, nil)
4956
return true
5057
#else
51-
write(signalWatchingPipe[1], &byte, 1)
58+
write(InterruptHandler.signalWatchingPipe[1], &byte, 1)
5259
#endif
5360
}
5461
#if os(Windows)
@@ -57,25 +64,25 @@ public final class InterruptHandler {
5764
var readPipe: HANDLE?
5865
var writePipe: HANDLE?
5966
let rv = CreatePipe(&readPipe, &writePipe, nil, 1)
60-
signalWatchingPipe = [readPipe!, writePipe!]
67+
Self.signalWatchingPipe = [readPipe!, writePipe!]
6168
guard rv else {
6269
throw SystemError.pipe(Int32(GetLastError()))
6370
}
6471
#else
6572
var action = sigaction()
6673
#if canImport(Darwin) || os(OpenBSD)
67-
action.__sigaction_u.__sa_handler = signalHandler
74+
action.__sigaction_u.__sa_handler = self.signalHandler
6875
#elseif os(Android)
69-
action.sa_handler = signalHandler
76+
action.sa_handler = self.signalHandler
7077
#else
7178
action.__sigaction_handler = unsafeBitCast(
72-
signalHandler,
79+
self.signalHandler,
7380
to: sigaction.__Unnamed_union___sigaction_handler.self)
7481
#endif
7582
// Install the new handler.
76-
sigaction(SIGINT, &action, &oldAction)
83+
sigaction(SIGINT, &action, &Self.oldAction)
7784
// Create pipe.
78-
let rv = TSCLibc.pipe(&signalWatchingPipe)
85+
let rv = TSCLibc.pipe(&Self.signalWatchingPipe)
7986
guard rv == 0 else {
8087
throw SystemError.pipe(rv)
8188
}
@@ -88,16 +95,16 @@ public final class InterruptHandler {
8895
var buf: Int8 = 0
8996
#if os(Windows)
9097
var n: DWORD = 0
91-
ReadFile(signalWatchingPipe[1], &buf, 1, &n, nil)
98+
ReadFile(Self.signalWatchingPipe[1], &buf, 1, &n, nil)
9299
#else
93-
let n = read(signalWatchingPipe[0], &buf, 1)
100+
let n = read(Self.signalWatchingPipe[0], &buf, 1)
94101
#endif
95102
// Pipe closed, nothing to do.
96103
if n == 0 { break }
97104
// Read the value of wasInterrupted and set it to false.
98-
let wasInt = wasInterruptedLock.withLock { () -> Bool in
99-
let oldValue = wasInterrupted
100-
wasInterrupted = false
105+
let wasInt = Self.wasInterruptedLock.withLock { () -> Bool in
106+
let oldValue = Self.wasInterrupted
107+
Self.wasInterrupted = false
101108
return oldValue
102109
}
103110
// Terminate all processes if was interrupted.
@@ -106,22 +113,22 @@ public final class InterruptHandler {
106113
}
107114
}
108115
#if os(Windows)
109-
CloseHandle(signalWatchingPipe[0])
116+
CloseHandle(Self.signalWatchingPipe[0])
110117
#else
111-
close(signalWatchingPipe[0])
118+
close(Self.signalWatchingPipe[0])
112119
#endif
113120
}
114121
thread.start()
115122
}
116123

117124
deinit {
118125
#if os(Windows)
119-
SetConsoleCtrlHandler(signalHandler, false)
126+
SetConsoleCtrlHandler(self.signalHandler, false)
120127
CloseHandle(signalWatchingPipe[1])
121128
#else
122129
// Restore the old action and close the write end of pipe.
123-
sigaction(SIGINT, &oldAction, nil)
124-
close(signalWatchingPipe[1])
130+
sigaction(SIGINT, &Self.oldAction, nil)
131+
close(Self.signalWatchingPipe[1])
125132
#endif
126133
thread.join()
127134
}

0 commit comments

Comments
 (0)