Discussing Signal handlers and logging in Python the question which functions are re-entrant in Python came up in my mind.
The signal library mention:
Although Python signal handlers are called asynchronously as far as the Python user is concerned, they can only occur between the atomic instructions of the Python interpreter. This means that signals arriving during long calculations implemented purely in C (such as regular expression matches on large bodies of text) may be delayed for an arbitrary amount of time.
That re-entrance is not typical is pointed out by the logging library:
If you are implementing asynchronous signal handlers using the signal module, you may not be able to use logging from within such handlers. This is because lock implementations in the threading module are not always re-entrant, and so cannot be invoked from such signal handlers.
I'm a little bit confused because the signal library talks about the GIL (global interpreter lock) as ".. between the atomic instructions ..". In this case signals are postponed and executed as soon as the GIL is left/unlocked. A kind of signal queue.
That makes sense but it does not matter if the functions which are called by the postponed signal handler are re-entrant because they are not called within the real POSIX signal handler with the "re-entrant"-limitation:
Only a defined list of POSIX C functions are declared as re-entrant and can be called within a POSIX signal handler. IEEE Std 1003.1 lists 118 re-entrant UNIX functions you find at https://www.opengroup.org/ (login required).
A function is said to be reentrant if there is a provision to interrupt the function in the course of execution, service the interrupt service routine and then resume the earlier going on function, without hampering its earlier course of action.
Non-reentrant functions are functions that cannot safely be called, interrupted, and then recalled before the first call has finished without resulting in memory corruption.
What Is Signaling In Python? In simplistic terms, a signal is an event. A signal is used to interrupt the execution of a running function. The signals are always executed in the main Python thread.
I believe that what makes the logging module non-reentrant is that it uses a threading.Lock
(instead of a RLock
) to synchronize several threads logging to the same handlers (so messages don't get interweaved).
This means that if a logging call which has acquired a lock is interrupted by a signal handler and that signal handlers tries to log it will deadlock forever waiting for the previous acquire
to be released.
These locks have nothing to do with the GIL by the way, they are "user created" locks to put it some way, the GIL is a lock used by the interpreter (an implementation detail).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With