For example, one kind of program organization that used to be more common in Unix but that remains legal involves keeping signals masked all the time except around certain blocking system calls, e.g. ppoll, that atomically unblock signals and wait. In such a program, a signal can arrive only inside the blocking system call and so the handler can call regular functions without the usual strictures of asynchronous signal safety. (Consider responding to SIGWINCH, which tells you about changing terminal size.)
Synchronous signals are also special in that they're, well, synchronous. That means that in the signal handler you can examine the target memory address or instruction pointer and take action specific to a given spot in your program --- e.g. longjmp to an error handler.
All of this is useful, safe, and legal under POSIX. The main problems with POSIX signals are that, 1) as this thread underscores, most people don't understand them, and 2) signal handlers are process global and hard to share. (Consider if you're running two VMs in a process and each wants to use a SIGSEGV GC safe point trick.)
(userfaultfd, sadly, requires more system calls than a synchronous signal handler to handle anomalous memory access.)
We should be enhancing POSIX signals to make them easier to share, not casting aspersions on them.
It's not. So what?
> The defining characteristics of signals is that, like interrupts and multi-threaded execution
No, they can't. You control when they are masked and unmasked. Certain signals are delivered in response only to certain actions (e.g. floating point errors when enabled, or memory access failures). It's not the chaos you think.
... As long as the called functions are fully async-signal-safe/reentrant. It used to be even more sensitive, in that not all register state was correctly saved/restored on Linux.
(On the fabled plan9, where signals are replaced with arbitrary-text "notes", the issue is even bigger as floating point registers are not saved/restored)
> We should be enhancing POSIX signals to make them easier to share, not casting aspersions on them.
That's fair, but they have already been fixed with signalfd, which more modern processes use to deal with the few POSIX-isms that require it, but many of the original uses have been superseded.
E.g., few modern applications use `timer_create` and SIGALRM, as they can instead use timerfd or poll with strategic timeout (although usually abstracted away by their eventloop). SIGBUS is a POSIX-way to deal with mmap truncation, but sealed memfds can be used to avoid the issue altogether. SIGUSR1/2 can be replaced by any IPC mechanism to give much more flexible controls than a single "reload"/"toggle" signal.
(The POSIX ways can still be useful in certain simpler programs of course.)
That is not accurate. If I have a single-threaded program sitting around a ppoll(2) loop and a signal can arrive only inside my main loop ppoll(), then I know a priori that my signal handler can't be interrupting non-reentrant code and so I can call whatever I want inside it.
> It used to be even more sensitive, in that not all register state was correctly saved/restored on Linux.
Don't confuse architectural flaws with implementation bugs. I'm reminded of an argument on emacs-devel in which someone argued that we couldn't call malloc(3) in a multi-threaded program because one beta version of glibc once had a thread safety bug in the malloc implementation.
> SIGBUS is a POSIX-way to deal with mmap truncation, but sealed memfds can be used to avoid the issue altogether.
Sealing a file descriptor doesn't physically seal a USB key into a USB port. :-) You also get SIGBUS on IO failures on mmaped files, and surprise removal is an IO failure. There's really no alternative to a synchronous signal here --- a regular file being mmap()ed isn't a userfaultfd.
IMHO, I think it's crying shame that Hotspot (last time I checked --- maybe it's fixed?) doesn't watch for SIGBUS on access to MappedByteBuffer and translate surprise USB device file removal into a nice clear VM-level exception.
Even for cases for which userfaultfd can work, a synchronous signal can be more efficient because it involves just one entry into the kernel. (sigreturn is optional.) I'd really hate to give up the conventional signal mechanism entirely, although of course I approve of things like signalfd and userfault that reduce the need for signal handling.