Erik Bray wrote:
At this point a potentially
waiting SIGINT is handled, resulting in KeyboardInterrupt being raised
while inside the with statement's suite, and finally block, and hence
Lock.__exit__ are entered.

Seems to me this is the behaviour you *want* in this case,
otherwise the lock can be acquired and never released.
It's disconcerting that it seems to be very difficult to
get that behaviour with a pure Python implementation.

I think it might be possible to
gain more consistency between these cases if pending signals are
checked/handled after any direct call to PyCFunction from within the
ceval loop.

IMO that would be going in the wrong direction by making
the C case just as broken as the Python case.

Instead, I would ask what needs to be done to make this
work correctly in the Python case as well as the C case.

I don't think it's even possible to write Python code that
does this correctly at the moment. What's needed is a
way to temporarily mask delivery of asynchronous exceptions
for a region of code, but unless I've missed something,
no such facility is currently provided.

What would such a facility look like? One possibility
would be to model it on the sigsetmask() system call, so
there would be a function such as

   mask_async_signals(bool)

that turns delivery of async signals on or off.

However, I don't think that would work. To fix the locking
case, what we need to do is mask async signals during the
locking operation, and only unmask them once the lock has
been acquired. We might write a context manager with an
__enter__ method like this:

   def __enter__(self):
      mask_async_signals(True)
      try:
         self.acquire()
      finally:
         mask_async_signals(False)

But then we have the same problem again -- if a Keyboard
Interrupt occurs after mask_async_signals(False) but
before __enter__ returns, the lock won't get released.

Another approach would be to provide a context manager
such as

   async_signals_masked(bool)

Then the whole locking operation could be written as

   with async_signals_masked(True):
      lock.acquire()
      try:
         with async_signals_masked(False):
            # do stuff here
      finally:
         lock.release()

Now there's no possibility for a KeyboardInterrupt to
be delivered until we're safely inside the body, but we've
lost the ability to capture the pattern in the form of
a context manager.

The only way out of this I can think of at the moment is
to make the above pattern part of the context manager
protocol itself. In other words, async exceptions are
always masked while the __enter__ and __exit__ methods
are executing, and unmasked while the body is executing.

--
Greg
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to