[Python-ideas] Async API
Steve Dower
Steve.Dower at microsoft.com
Thu Oct 25 01:25:15 CEST 2012
>On Wed, Oct 24, 2012 at 4:03 PM, Yury Selivanov <yselivanov.ml at gmail.com> wrote:
>> Hi Guido,
>>
>> On 2012-10-24, at 6:43 PM, Guido van Rossum <guido at python.org> wrote:
>>> What's the problem with just letting the cleanup take as long as it
>>> wants to and do whatever it wants? That's how try/finally works in
>>> regular Python code.
>
>> The problem appears when you add timeouts support.
>>
>> Let me show you an abstract example (I won't use yield_froms, but I'm
>> sure that the problem is the same with them):
>>
>> @coroutine
>> def fetch_comments(app):
>> session = yield app.new_session()
>> try:
>> return (yield session.query(...))
>> finally:
>> yield session.close()
>>
>> and now we execute that with:
>>
>> #: Get a list of comments; throw a TimeoutError if it
>> #: takes more than 1 second
>> comments = yield fetch_comments(app).with_timeout(1.0)
>>
>> Now, scheduler starts with 'fetch_comments', then executes
>> 'new_session', then executes 'session.query' in a round-robin fashion.
>>
>> Imagine, that database query took a bit less than a second to execute,
>> scheduler pushes the result in coroutine, and then a timeout event occurs.
>> So scheduler throws a 'TimeoutError' in the coroutine, thus preventing
>> the 'session.close' to be executed. There is no way for a scheduler
>> to understand, that there is no need in pushing the exception right
>> now, as the coroutine is in its finally block.
>>
>> And this situation is a pretty common when you have such timeouts
>> mechanism in place and widely used.
>
>Ok, I can understand. But still, this is a problem with timeouts in general, not just with timeouts in a yield-based environment. How does e.g. Twisted deal with this?
>
>As a work-around, I could imagine some kind of with-statement that tells the scheduler we're already in the finally clause (it could still send you a timeout if your cleanup takes way too long):
>
>try:
> yield <regular code>
>finally:
> with protect_finally():
> yield <cleanup code>
>
>Of course this could be abused, but at your own risk -- the scheduler only gives you a fixed amount of extra time and then it's quits.
>
>
Could another workaround be to spawn the cleanup code without yielding - in effect saying "go and do this, but don't come back"? Then there is nowhere for the scheduler to throw the exception.
I ask because this falls out naturally with my implementation (code is coming, but work is taking priority right now): "do_cleanup()" instead of "yield do_cleanup()". I haven't tried it in this context yet, so no idea whether it works, but I don't see why it wouldn't. In a system without the @async decorator you'd need a "scheduler.current.spawn(do_cleanup)" instead of yield [from]s, but it can still be done.
Cheers,
Steve
More information about the Python-ideas
mailing list