4 ms·
POSIX signals are broken by design, alas: https://lwn.net/Articles/414618/ https://lwn.net/Articles/414618/ Python or not, almost nothing is safe inside a sign
by nine_k 1mo ago
POSIX signals are broken by design, alas: https://lwn.net/Articles/414618/ https://lwn.net/Articles/414618/
Python or not, almost nothing is safe inside a signal handler.
- knome 1mo agothe first line of the article points out that python isn't run in the POSIX C handler. that just sets a flag for the interpreter to act on. the python issue is an unsafe re-entrant handling strategy in the interpreter.
- masklinn 1mo agoAnd this is explained in more details in the first article of the series.
- zaphirplane 1mo agoToo funny
- inigyou 1mo agoCertain signals are true user-mode interrupts, like SIGTERM and some of the ones glibc uses to implement pthreads. The ones related to application logic must only be handled with signalfd if you want any semblance of reliability.
- pjmlp 1mo agoExactly, the only safe thing to do in a signal is set a variable for other code to react on, also they completely mess up in multi-threaded code.
- germandiago 1mo agoThat is exactly what I do: set an stomic variable and do yhings outside of the handler.
- masklinn 1mo agoAnd that is pretty much what Python does for you, the Python-level signal handlers are not C signal handlers. Python sets a C signal handler to set an interpreter variable then exit, then it calls the Python level signal handler on the main thread (and on the main thread only, which can be a concern in sone cases).
- krackers 1mo ago>almost nothing is safe That's a bit of an overstatement? There's a list of things that you _can_ call, and fairly useful ones too like `write` https://man7.org/linux/man-pages/man7/signal-safety.7.html https://man7.org/linux/man-pages/man7/signal-safety.7.html
- Joker_vD 1mo agoThere is also a charming statement in POSIX standard that If the signal occurs other than as the result of calling abort(), raise(), [CX] [Option Start] kill(), pthread_kill(), or sigqueue(), [Option End] the behavior is undefined if the signal handler refers to any object with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t, or if the signal handler calls any function in the standard library other than one of the functions listed in Signal Concepts. You literally can't read any global/static variables and you can only write to global/static variables that are declared to be volatile sig_atomic_t. This tremendously shrinks the amount of useful work you can do with the signal-safe functions from the standard library.
- nottorp 1mo agoIt may help if you think of them as interrupts and not something that comes in your message queue.
- Joker_vD 1mo agoNo, I understand that. I just find it deeply ironic that when an interrupt/signal arrives, pretty much the only thing you can do to handle it, is to raise some flag, then leave the handler and continue doing whatever you were doing in a message loop. Like, why even bother with supporting function callbacks in sigaction() etc? Just have each thread have a chunk of volatile memory where the kernel writes info about the arrived signals, and that's it, that's your signal handling framework. In fact, here is another, a very fresh, example from POSIX: [0]. There is an example at how to use SIGWINCH signal handler with tcgetwinsize(). Just look at this thing of terrible beauty, notice that SIG_ATOMIC_MAX is not required to bigger than a byte's worth of data, and also read the whole of "APPLICATION USAGE" section. "Multi-threaded applications should avoid the signal handler idiom in general", gee, I wonder why. And of course, the signal may never be generated in the first place, so "[s]uch processes must periodically poll the current terminal window size if needed". What a solid technical foundation to build race-free, bug-free applications on top of. [0] https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcgetwinsize.html https://pubs.opengroup.org/onlinepubs/9799919799/functions/t...