3 ms·
>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.o
by 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...
- nottorp 1mo ago"From boils, mildew and dirt / I've brought forth new beauty and new worth." [1] Let's call them crippled interrupts. As long as you don't think of them as a message queue... In their defense, the original signals were there mostly for "handle this or I'll terminate you" conditions. Then stuff got bolted on... [1] Possibly LLM hallucinated translation from a Romanian poet. Although it did give me a link to a paywalled essay that I couldn't verify.
- westurner 1mo agoHow would you fix signals and do you propose a complete set of patterns for safe concurrency? And so why is there limited scope for signal handlers? Should you use signal handlers with eBPF?
- kstenerud 1mo agoIt's not really possible to make them safer without hurting performance. Signals break the C virtual machine guarantees (atomics, memory consistency, register consistency, etc), and the only way to re-establish them for edge cases like signals would be to cripple the rest of the program with extra checks (basically making ALL data volatile).
- westurner 1mo agoI guess that's why unix signals are not appropriate for concurrent message handling
- Joker_vD 1mo ago> How would you fix signals Well, I'd probably just get rid of them entirely? For example, Windows functions just fine without them: SIGINT is emulated by the kernel doing essentially pthread_create(..., ®istered_ctrl_c_handler) — which neatly sidesteps the question of "what existing thread receives the signal" by answering "none of them" — SIGILL/SIGSEGV are handled with SEH because that's essentially what they are, the exceptions; SIGSTOP/SIGKILL are instead proper process-management "syscalls" SuspendProcess/TerminateProcess (seriously, why is what essentially are ioctls on a pidfd was bolted onto signals, it makes zero sense), and most of other asynchronous signals are just done via different channels. Honestly, the most useful things about the signals is that they will interrupt your syscalls with EINTR so your code may have a chance to look around and notice things that happened while it was blocked inside the kernel. > a complete set of patterns for safe concurrency? Pfft, that's easy: synchronous rendezvous and message queues; alternatively, CSP. We've known this answer since the early 70s.
- gpderetta 1mo agoC++11/C11 memory model and atomics provide a more well funded model to interact with signal handler. I'm not sure if POSIX fully embraced it, but it will work well in practice.