What’s really fun is mixing signal handling and threads, especially on Linux. There is a simple way to do it and about a thousand ways that include at least one gotcha.
He considers it safe if it's unlikely to crash? That's also true in C. Calling printf in a C signal handler is likely to work. So why does he consider it important in C but "not a practical consideration" in Python? It's more likely to crash in Python than in C because the signal handler takes longer to execute.
The article mentions that the Python handler is run outside of the C handler context and so is not subject to the C safety restrictions. It will not crash since the interpreter only calls the Python handler when it is safe. It will however not protect against reentrancy issues in the Python handler.
And when you combine it with event loops and multiple OSes + python versions it gets even more difficult. Don't get me wrong: I love python, but shut down / cleanup is kind of a pain in the ass. If someone built a (good) lib for this it would probably be quite popular.
the 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.
And 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).
There 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.
C++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.
I don't understand how this is still a problem in 2026. Signals should just come in via a new thread and it would solve all the complexity around them. Everyone has known the current way it works is extremely limited in what you can safely do. This whole pause the execution of what's currently running and then run some extra code somewhere else turned out to not be a good idea.
That is also good, but requires apps be rewritten to read from signalfd. With the separate thread approach you can get away without having to rewrite programs.
In practice I don't think there would be that many. You could even pause execution of the thread that would have gotten the signal to make it even safer.
I think the author undersells the significance of this. I could easily picture someone writing code that boils down to the example, all it requires is a flood of signals and a print statement.
If your program generates signals, it could generate a flood unintentionally. If you put a print statement in the handler, you can get this result.
31 comments
[ 0.24 ms ] story [ 10.3 ms ] threadThe C printf function is not async signal safe and is one of the examples in the manual: https://man7.org/linux/man-pages/man7/signal-safety.7.html
Python or not, almost nothing is safe inside a signal handler.
The ones related to application logic must only be handled with signalfd if you want any semblance of reliability.
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
If your program generates signals, it could generate a flood unintentionally. If you put a print statement in the handler, you can get this result.
It's not terribly surprising, but good to know.