Add workaround for usleep throwing SIGALRM signals

On Solaris, usleep is not thread-safe and will introduce a race
condition when used in a multithreaded environment. This manifests as
SIGALRM signals escaping from its internal call to sigsuspend. If a
signal is missing a handler, the process dies upon receiving it,
crashing.

The Solaris libc code does something rougly equivalent to this:

    PreserveSignalHandlers(...);
    /* Effectively NOPs out SIGALRM */
    DisableSIGALRM(...);
    SetTimer(...);

    /* Suspends the thread until it receives a SIGALRM signal. When it
       receives this signal, it both wakes up *and* receives the
       signal. In this case we don't want to do anything but wake up,
       so the SIGALRM handler is a NOP.
    */

    sigsuspend(...);

    RestoreSignalHandlers(...);

    return;

The consequences of this code in a multithreaded context is that if
two sleeps are in progress at the same time, one will necessarily
finish before the other, *restore the non-existant handlers*, and go
on doing its thing. When the other sleep finishes, it won't have a NOP
handler to call when waking up, a handler is searched for, none is
found, and thus we crash.
This commit is contained in:
John Lorentzson 2025-04-24 20:32:22 +02:00
parent a86f5df428
commit a56c9c57a2

View file

@ -11,6 +11,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>
#include <cstring>
#include <sys/time.h>
@ -24,6 +25,8 @@ Visual* xVisual;
static XVisualInfo xvisinfo;
static unsigned long startTime;
static void nophandler(int signo) {}
bool Backend_Init(void (*drag_and_drop_callback)(const char *path), void (*window_focus_callback)(bool focus)) {
// we're ignoring the hell out of focus and drag & drop
xDisplay = XOpenDisplay(NULL);
@ -35,6 +38,17 @@ bool Backend_Init(void (*drag_and_drop_callback)(const char *path), void (*windo
gettimeofday(&time, NULL);
startTime = (time.tv_sec * 1000) + (time.tv_usec / 1000);
// Disable SIGALRM to compensate for thread-unsafe usleep in Solaris
sigset_t mask;
struct sigaction act;
struct sigaction oldact;
act.sa_handler = nophandler;
act.sa_flags = 0;
sigaction(SIGALRM, &act, &oldact);
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigprocmask(SIG_BLOCK, &mask, 0);
return true;
}