How to explain the reentrant RuntimeError caused by printing in signal handlers? How to explain the reentrant RuntimeError caused by printing in signal handlers? python python

How to explain the reentrant RuntimeError caused by printing in signal handlers?


You might prefer to inhibit delivery of SIGINT to the child, so there's no race,perhaps by putting it in a different process group,or by having it ignore the signal.Then only SIGTERM from the parent would matter.

To reveal where it was interrupted, use this:

    sig_num, frame = args    print(dis.dis(frame.f_code.co_code))    print(frame.f_lasti)

The bytecode offsets in the left margin correspond tothat last instruction executed offset.

Other items of interest includeframe.f_lineno,frame.f_code.co_filename, andframe.f_code.co_names.

This issue becomes moot in python 3.7.3, which no longer exhibits the symptom.


Signals are processed between opscode(see eval_frame_handle_pending()in python's opscode processor loop), but not limited to it. print is a perfect example. It is implemented based on _io_BufferedWriter_write_impl(), which has a structure like

ENTER_BUFFERED() => it locks buffer

PyErr_CheckSignals() => it invoke signal handler

LEAVE_BUFFERED() => it unlocks buffer

by calling PyErr_CheckSignals(), it invoke another signal handler, which has another print in this case. The 2nd print run ENTER_BUFFERED() again, because the buffer is already locked by previous print in 1st signal handler, so the reentrant exception is thrown as below snippet shows.

    // snippet of ENTER_BUFFERED    static int    _enter_buffered_busy(buffered *self)    {        int relax_locking;        PyLockStatus st;        if (self->owner == PyThread_get_thread_ident()) {            PyErr_Format(PyExc_RuntimeError,                         "reentrant call inside %R", self);            return 0;        }    }        #define ENTER_BUFFERED(self) \        ( (PyThread_acquire_lock(self->lock, 0) ? \           1 : _enter_buffered_busy(self)) \         && (self->owner = PyThread_get_thread_ident(), 1) )

P.S.

Reentrant Functions from Advanced Programming in the Unix Environment.

The Single UNIX Specification specifies the functions that are guaranteed to be safe to call from within a signal handler. These functions are reentrant and are called async-signal safe. Most of the functions that are not reentrant because

  1. they are known to use static data structures,
  2. they call malloc or free
  3. they are part of the standard I/O library. Most implementations of the standard I/O library use global data structures in a nonreentrant way. print in Python belongs to this category.