Skip to content

Commit 3b201cc

Browse files
committed
Workaround a lib dispatch behavior that made the delay blocking strategy not work on Linux
1 parent f85b045 commit 3b201cc

File tree

1 file changed

+21
-11
lines changed

1 file changed

+21
-11
lines changed

Sources/SignalHandling/DelayedSigaction/SigactionDelayer_Block.swift

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,12 @@ and a thread is stuck with a pending signal forever… (I have actually not
3636
observed another behavior on macOS.)
3737

3838
**On Linux**, the system sends the signal to _all_ threads and assigns the
39-
thread as soon as one is ready to handle the signal, but when the signal is
40-
allowed to go through via `sigsuspend`, it seems the `libdispatch` catches it _a
41-
second time_! (I’m not sure why though.) Furthermore, `libdispatch` overrides
42-
the sigaction for a given signal when said signal is registered for monitoring.
43-
44-
So to make things work in Linux with this strategy, we’d have to detect the
45-
signals received in `libdispatch` because of the call to `sigsuspend`, and also
46-
“save” the sigaction somehow and reset it to the original non-overridden by
47-
libdispatch value, before calling `sigsuspend` (basically, we’re getting closer
48-
to the Unsig strategy…). We’d get a lot of race conditions (AFAICT). I really
49-
don’t think this work is worth the trouble. Just use the `Unsig` strategy.
39+
thread as soon as one is ready to handle the signal, so this strategy works.
40+
However, contrary to what the man page says, it seems `libdispatch` _does_
41+
modify the sigaction when a signal source is installed. It seems saving the
42+
sigaction prior to registering the signal source, then setting it back after the
43+
registration of the source fixes the issue though, so we did that. This solution
44+
seems weak though, and might break in the future.
5045

5146
- Important: An important side-effect of this technique is if a bootstrapped
5247
signal is then sent to a specific thread, the signal will be blocked. Forever.
@@ -245,6 +240,10 @@ public enum SigactionDelayer_Block {
245240
if let ds = blockedSignals[signal] {
246241
blockedSignal = ds
247242
} else {
243+
#if os(Linux)
244+
let currentSigaction = try Sigaction(signal: signal)
245+
#endif
246+
248247
try executeOnThread(.block(signal))
249248

250249
let dispatchSourceSignal = DispatchSource.makeSignalSource(signal: signal.rawValue, queue: signalProcessingQueue)
@@ -255,6 +254,17 @@ public enum SigactionDelayer_Block {
255254
dispatchSourceSignal.setEventHandler{ processSignalsOnQueue(signal: signal, count: dispatchSourceSignal.data) }
256255
dispatchSourceSignal.activate()
257256

257+
#if os(Linux)
258+
/* On Linux, the sigaction must be reset after a dispatch source signal
259+
 * is registerd because libdispatch _does_ modify the sigaction.
260+
 * https://github.com/apple/swift-corelibs-libdispatch/pull/560 */
261+
do {try currentSigaction.install(on: signal)}
262+
catch {
263+
dispatchSourceSignal.cancel()
264+
throw error
265+
}
266+
#endif
267+
258268
blockedSignal = BlockedSignal(dispatchSource: dispatchSourceSignal)
259269
}
260270

0 commit comments

Comments
 (0)