[Python-ideas] Asynchronous exception handling around with/try statement borders

Nathaniel Smith njs at pobox.com
Thu Jun 29 01:05:33 EDT 2017


On Wed, Jun 28, 2017 at 9:14 PM, Greg Ewing <greg.ewing at 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


More information about the Python-ideas mailing list