@@ -7,7 +7,8 @@ import SystemPackage
77/**
88A way to delay the sigaction for a given signal until arbitrary handlers have
99allowed it. Use with care. You should understand how the delaying works before
10- using this.
10+ using this. In particular **this method does not work at all on Linux**. Unless
11+ not possible, you should really use the `Unsig` strategy.
1112
1213First, to delay a sigaction, you have to bootstrap the signals you’ll want to
1314delay, **before any other threads are created**. Technically you can bootstrap
@@ -25,11 +26,27 @@ When the first handler is registered for a given signal, we instruct the thread
2526to block the given signal. Whenever the signal is received, we are notified via
2627GCD, and then tell the thread to receive the signal via `sigsuspend`.
2728
28- A (big) caveat: _From what I understand_, when all threads are blocking a given
29+ - Important: Some (big) caveats.
30+
31+ **On macOS**, _from what I understand_, when all threads are blocking a given
2932signal, the system has to choose which thread to send the signal to. And it
3033might not be the one we have chosen to process signals… so we sometimes have to
3134re-send the signal to our thread! In which case we lose the info in `siginfo_t`,
32- and a thread is stuck with a pending signal forever…
35+ and a thread is stuck with a pending signal forever… (I have actually not
36+ observed another behavior on macOS.)
37+
38+ **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.
3350
3451- Important: An important side-effect of this technique is if a bootstrapped
3552signal is then sent to a specific thread, the signal will be blocked. Forever.
@@ -389,6 +406,7 @@ public enum SigactionDelayer_Block {
389406 /* Only suspend process if signal is not ignored or
390407 * sigsuspend would not return. I know there is a race
391408 * condition. */
409+ // loggerLessThreadSafeDebugLog("🧵 Calling sigsuspend for \(signal)")
392410 sigsuspend ( & sigset)
393411 }
394412
0 commit comments