Skip to content

Commit d101c42

Browse files
committed
Add comments about the block strategy
1 parent a890055 commit d101c42

File tree

2 files changed

+31
-10
lines changed

2 files changed

+31
-10
lines changed

Readme.adoc

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,13 @@ dedicated thread too!
117117
When the signal is received, `libdispatch` will notify the delayer, which will
118118
unblock the signal, thus allowing it to be delivered.
119119

120-
**Caveat of this method**: On macOS, when a signal blocked on all threads is
121-
received, it seems to be affected to an arbitrary thread. Unblocking the signal
122-
on another thread will not unblock it at all. To workaround this problem we
123-
check if the signal is pending on the dedicated thread before unblocking it. If
124-
it is not, we send the signal to our thread, thus losing the sigaction again,
125-
exactly like when using the unsigaction strategy. Plus the original signal will
126-
stay pending on the affected thread forever.
120+
**Caveats of this method**:
121+
122+
- On macOS, when a signal blocked on all threads is received, it seems to be
123+
assigned to an arbitrary thread. Unblocking the signal on another thread will
124+
not unblock it at all. To workaround this problem we check if the signal is
125+
pending on the dedicated thread before unblocking it. If it is not, we send the
126+
signal to our thread, thus losing the sigaction again, exactly like when using
127+
the unsigaction strategy. Plus the original signal will stay pending on the
128+
affected thread forever.
129+
- On Linux this strategy cannot really work. See comments in the source.

Sources/SignalHandling/DelayedSigaction/SigactionDelayer_Block.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import SystemPackage
77
/**
88
A way to delay the sigaction for a given signal until arbitrary handlers have
99
allowed 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

1213
First, to delay a sigaction, you have to bootstrap the signals you’ll want to
1314
delay, **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
2526
to block the given signal. Whenever the signal is received, we are notified via
2627
GCD, 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
2932
signal, the system has to choose which thread to send the signal to. And it
3033
might not be the one we have chosen to process signals… so we sometimes have to
3134
re-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
3552
signal 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

Comments
 (0)