+2026-05-16 Bruno Haible <bruno@clisp.org>
+
+ fatal-signal: Make really multithread-safe.
+ * lib/fatal-signal.h (block_fatal_signals): Document a constraint
+ regarding thread creation.
+ * lib/fatal-signal.c: Include sigdelay.h.
+ (fatal_signals_block_initially_mt): New variable.
+ (block_fatal_signals, unblock_fatal_signals): In a multithreaded
+ process, use sigdelay instead of pthread_sigmask.
+ * modules/fatal-signal (Depends-on): Add sigdelay.
+
2026-05-16 Bruno Haible <bruno@clisp.org>
thread-optim: Add tests.
#include "glthread/lock.h"
#include "glthread/once.h"
#include "thread-optim.h"
+#include "sigdelay.h"
#include "sig-handler.h"
/* ========================================================================= */
to occur in different threads and even overlap in time. */
gl_lock_define_initialized (static, fatal_signals_block_lock)
static unsigned int fatal_signals_block_counter = 0;
+/* For correct operation in the face of thread-optim.h. */
+static bool fatal_signals_block_initially_mt;
/* Temporarily delay the catchable fatal signals. */
void
if (fatal_signals_block_counter++ == 0)
{
+ fatal_signals_block_initially_mt = mt;
init_fatal_signal_set ();
- pthread_sigmask (SIG_BLOCK, &fatal_signal_set, NULL);
+ if (mt)
+ sigdelay (SIG_BLOCK, &fatal_signal_set, NULL);
+ else
+ pthread_sigmask (SIG_BLOCK, &fatal_signal_set, NULL);
+ }
+ else
+ {
+ if (!fatal_signals_block_initially_mt && mt)
+ {
+ /* The process was single-threaded and has become multithreaded
+ before the matching unblock_fatal_signals() call. This is
+ a constraint violation. */
+ abort ();
+ }
}
if (mt) gl_lock_unlock (fatal_signals_block_lock);
abort ();
if (--fatal_signals_block_counter == 0)
{
+ if (!fatal_signals_block_initially_mt && mt)
+ {
+ /* The process was single-threaded and has become multithreaded
+ at the matching unblock_fatal_signals() call. This is a
+ constraint violation. */
+ abort ();
+ }
init_fatal_signal_set ();
- pthread_sigmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
+ if (fatal_signals_block_initially_mt)
+ sigdelay (SIG_UNBLOCK, &fatal_signal_set, NULL);
+ else
+ pthread_sigmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
}
if (mt) gl_lock_unlock (fatal_signals_block_lock);
/* Temporarily delay the catchable fatal signals.
The signals will be blocked (= delayed) until the next call to
unblock_fatal_signals(). If the signals are already blocked, a further
- call to block_fatal_signals() has no effect. */
+ call to block_fatal_signals() has no effect.
+ The program must obey the following constraint: If at the moment of a
+ block_fatal_signals() call the process is single-threaded, it MUST NOT
+ create additional threads until the matching unblock_fatal_signals()
+ call. */
extern void block_fatal_signals (void);
/* Stop delaying the catchable fatal signals. */