
On Wed, Jun 28, 2017 at 9:14 PM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Nathaniel Smith wrote:
So what should this async_signals_masked state do when we yield out from under it? If it's a thread-local, then the masking state will "leak" into other async function callstacks (or similar for regular generators), which is pretty bad. But it can't be just frame-local data either, because we want the masking state to be inherited by and subroutines we call from inside the masked block.
That should be easy enough, shouldn't it? When entering a new frame, copy the mask state from the calling frame.
Right, that approach would be semantically equivalent to the walking-the-call-stack approach, just it shifts some of the cost around (it makes signals cheaper by making each function call a tiny bit more expensive).
For example, we could have a flag on a function frame that says "this frame (and the code in it) should not be interrupted", and then in the bytecode loop when a signal arrives, walk up the call stack to see if any of these flags are set before running the Python-level signal handler.
That would make it impossible to temporarily unmask async signals in a region where they're masked.
An example of a situation where you might want to do that is in an implementation of lock.acquire(). If the thread needs to block while waiting for the lock to become available, you probably want to allow ctrl-C to interrupt the thread while it's blocked.
So trio actually does allow this kind of nesting -- for any given frame the special flag can be unset, set to True, or set to False, and the signal handler walks up until it finds the first one frame where it's set and uses that. So I guess the equivalent would be two flags, or a little enum field in the frame object, or something like that.
This would make me nervous, because context managers are used for all kinds of things, and only some of them involve delicate resource manipulation.
The next step I had in mind was to extend the context manager protocol so that the context manager can indicate whether it wants async signals masked, so it would only happen for things like lock that really need it.
A magic (implemented in C) decorator like @async_signals_masked I think would be the simplest way to do this extension. (Or maybe better call it @no_signal_delivery, because it would have to block all running of signal handlers; the interpreter doesn't know whether a signal handler will raise an exception until it calls it.) -n -- Nathaniel J. Smith -- https://vorpus.org