[0] https://web.mit.edu/~simsong/www/ugh.pdf, page 313
There's a lesson here, I think: "worse is better" is good advice when it lets you ship and get software into the hands of customers quicker, but it doesn't change the fact that you should do the "right thing" at some point.
For example, signal handling is completely broken in Python since blocking syscalls (think `select`, but see `signal(7)` for a complete list) will not be interrupted.
`SA_RESTART` correctly excludes such syscalls so you can properly handle the EINTR instead of hanging.
For regular files, Linux has a simple solution for you: You'll never get EINTR. It's only a thing for sockets, pipes and other things that can block indefinitely, which a file cannot.
(Except if you're on NFS, in which case you have to choose between two evils depending on whether your file system is mounted intr or nointr :-) )
(You don't need to reboot, though; you can “mount -o remount,intr …” to switch states and then kill. I wonder if you can also now actually do kill -9 specifically even on nointr, but I haven't checked.)
Potentially, there could be other ways of dealing with communication, some of them already exist in popular operating systems, s.a. sockets. It's actually funny that you mention one in your problem statement. Erlang-style ports are another possible solution.
Poll + an eventfd (or another socket). IMO, signals create more problems than they solve.
Not really. A blocking poll() call on a single socket (+ eventfd) is still blocking I/O.
Yeah, but what if the thread is not currently blocked and you want to interrupt it / stop the thread? Unfortunately, it doesn't queue the EINTR for the next blocking function if the thread was NOT blocked at the moment the signal was sent.
Except the standard logger route my isn’t signal safe, nor much of the basic library routines one would want to use!
The only sane solution is to block/mask all signals, perhaps polling for them if really needed. Anything else is on the path to insanity.
https://docs.python.org/3/library/signal.html#execution-of-p...
Here's how Python does it.
The low-level C handler set the flag then returns. Only some time later does the Python run-time call the associated Python handler that you see in the Examples section.
https://github.com/python/cpython/blob/5f7aba938cf5007b6f954...
Here's even a test for it: https://github.com/python/cpython/blob/5f7aba938cf5007b6f954...
The Right Thing is to make system call submission atomic and asynchronous, only waiting on completion by explicit choice, and remove signals entirely in favor of buffered message-passing IPC. This is basically the world we're approaching with io_uring and signalfd, except for ugly interaction with coalescing and signal dispositions (see https://ldpreload.com/blog/signalfd-is-useless), and the fact that many syscalls still can't be performed through io_uring.
If UNIX had a better API for process management, people wouldn't see signals as necessary, but that's its own can of worms with its own Linux-specific partial fix (pidfd) and genre of gripe article (e.g. https://news.ycombinator.com/item?id=35264487).