[Python-ideas] Protecting finally clauses of interruptions

Paul Colomiets paul at colomiets.name
Tue Apr 3 00:23:31 CEST 2012


Hi Yury,

>On 2012-04-02, at 4:49 PM, Paul Colomiets wrote:
>> l.lock()
>> try:
>>    ...
>> finally:
>>    l.unlock()
>>
>> Which will break if you interrupted just after lock is acquired.
>
>I guess the best way to solve this puzzle, is to track all locks that
>the thread acquires and release them in case of forced interruption.

Same with open files, and with all other kinds of contexts. I'd go
he route of making __enter__ also uninterruptable (and make timeout
inside a lock itself).

On Tue, Apr 3, 2012 at 12:15 AM, Yury Selivanov <yselivanov.ml at gmail.com> wrote:
>>> 3. Either add a special base exception, that can be thrown in a currently
>>> executing frame to interrupt it, or add a special method to frame object
>>> 'f_interrupt()'. Once a frame is attempted to be interrupted, it checks
>>> its 'f_in_finally' counter.  If it is 0, then throw exception, if not -
>>> wait till it sets back to 0 and throw exception immediately.
>>>
>>
>> Not sure how it supposed to work. If it's coroutine it may yield
>> while in finally, and you want it be interrupted only when it exits from
>> finally.
>
> And what's the problem with that?  It should be able to yield in its
> finally freely.
>
> @coroutine
> def read_data(connection):
>  try:
>    yield connection.recv()
>  finally:
>    yield connection.close()
>  print('this shouldn't be printed if a timeout occurs')
>
> yield read_data().with_timeout(0.1)
>
> In the above example, if 'connection.recv()' takes longer than 0.1s to
> execute, the scheduler (trampoline) should interrupt the coroutine,
> 'connection.abort()' line will be executed, and once connection is
> aborted, it should stop the coroutine immediately.
>
> As of now, if you throw an exception while generator is in its 'try'
> block, everything will work as I explained.  The interpreter will
> execute the 'finally' block, and propagate the exception at the end
> of it.
>
> However, if you throw an exception while generator in its 'finally'
> block (!), then your coroutine will be aborted too early.  With your
> 'f_in_finally' flag, scheduler simply won't try to interrupt the
> coroutine, but, then the 'print(...)' line will be executed (!!)
> (and  it shouldn't really).  So, we need to shift the control of when
> a frame is best to be interrupted to the interpreter, not the user
> code.

You've probably not explained your proposal well.

If I call frame.f_interrupt() what should it do? Return anything
yield from a generator? And how you supposed to continue
generator iteration in this case? Or are you going to iterate
result of `f_interrupt()`? What it should do if it's not topmost
frame?

In all my use cases it doesn't matter if "print" is executed, just
like it doesn't matter if timeout occured after 1000 ms or
after 1001 or 1010 ms or even after 1500 ms as it actually
could. So sleeping a bit and trying again is OK. You need
to make all __exit__ and finally clauses fast, but it usually
not a problem.

--
Paul



More information about the Python-ideas mailing list