Hi. I think your proposed patch makes sense. To be honest, I hadn't even noticed that task cancellation was asynchronous.
One could argue that the unit tests could be fixed instead of wait_for, but I don't think there's anything to be gained by this. If we don't lose performance by wrapping things nicely before returning from the wait_for call, then we might as well just do it, IMHO. Less headaches for the Tulip user. Thanks. On 7 February 2014 15:26, Victor Stinner <[email protected]> wrote: > Hi, > > I have a complex issue in Trollius, and I just realized that Tulip has > the same behaviour (but the consequence are less important). Do you > think that the issue should be fixed in Tulip too? I mean apply this > patch to Tulip: > https://bitbucket.org/enovance/trollius/commits/e8e82dc > > Ok, now let's see the whole story. > > * * * > > The following script displays "Task cancelled on timeout? False": > --- > import asyncio > > @asyncio.coroutine > def coro(): > task = asyncio.async(asyncio.wait_for(asyncio.sleep(1.0), 0.1)) > try: > yield from task > except asyncio.TimeoutError: > print("Task cancelled on timeout? %s" % task.cancelled()) > > loop = asyncio.get_event_loop() > loop.run_until_complete(coro()) > --- > > The problem is that wait_for() calls fut.cancel(), but Task.cancel() > cancels itself asynchronously if the task is waiting for a Future > object. There is a pending call to Task._wakeup() which will be > executed later. > > I found this surprising behaviour while trying to fix an issue in > Trollius: test_wait_for() fails with Trollius. > > I modified wait_for() in Trollius to wait until the task is really > cancelled, changset: > https://bitbucket.org/enovance/trollius/commits/e8e82dc > > * * * > > In Trollius, the situation is worse than Tulip because there are more > links between futures and tasks. See this example: > --- > @asyncio.coroutine > def task2(): > yield [from] asyncio.sleep(1.0) > > @asyncio.coroutine > def task1(): > yield [from] task2() > --- > > With Tulip, task1() waits for sleep internal future: > > task1 => sleep future > > With Trollius, task1() waits for task2() which itself waits for sleep > which waits for sleep internal future: > > task1 => task2 => sleep => sleep future > > Cancelling task1 is first propagated synchronously to the left (to > child futures), and then come back asynchronously to the parents (to > task1). > > In Tulip, test_wait_for() test pass because there is just one pending > call to cancel task1. In Trollius, it takes more roundtips of event > loop, because each cancellation requires one loop iteration. > > Victor > -- Gustavo J. A. M. Carneiro Gambit Research LLC "The universe is always one step beyond logic." -- Frank Herbert
