On Thu, Jan 16, 2014 at 9:03 AM, Victor Stinner
<[email protected]> wrote:
> I'm writing documentation to explain common traps with asynchronous
> programming (with asyncio).

Thanks!

> While working on an example, I realized
> that asyncio doesn't emit warnings when a task is scheduled before
> never executed.

That's because this is a common case.

> It's possible to detect when a coroutine is called but not scheduled,
> detect when an exception is not consumed, but I don't see how to
> detect when tasks are still scheduled at exit.

Technically, the event loop doesn't know about tasks, only about callbacks.

Using implementation details you could check loop._ready (callbacks
that are ready to run -- every callback must be put here before it is
called) and loop._scheduled (callbacks with a delay still to be
waited). But I agree that's a bad idea.

> Well, there is Task.all_tasks(loop), but it doesn't give the traceback
> where the task was created,

That sounds like a separate useful debugging feature -- optionally
record exactly where a task was created.

> and it doesn't include other scheduled callbacks.

That's because the event loop doesn't know about tasks. :-)

Note that there's a third category which is even more elusive -- I/O callbacks.

But I suppose we can't help that. The event loop doesn't directly
manage these -- they are managed by the selector, which has its own
rules. I think closing a selector while you still have open FDs is a
valid use case -- there are places where a selector is temporarily
created to do some waiting in an otherwise mostly synchronous app, see
e.g. telnetlib.py.

> Example:
> ---
> import asyncio
> asyncio.tasks._DEBUG = True
>
> @asyncio.coroutine
> def important_task():
>     yield from asyncio.sleep(1.0)
>     print("import task")
>
> def stop():
>     loop.stop()
>
> loop = asyncio.get_event_loop()
> loop.call_later(0.1, stop)
> asyncio.async(important_task())
> loop.run_forever()
> print("loop is stopped")
> loop.close()
> ---
>
> "import task" is not displayed because the task never completes.
> Imagine that this task flushs important data on disk before exit.

Well, you either shouldn't rely on it (what if the process is killed
hard or the machine crashes hard) or at least you shouldn't be calling
stop().

> BaseEventLoop.stop() documentation says:
> "Every callback scheduled before stop() is called will run. Callback
> scheduled after stop() is called won’t."

Sorry, that's a bit ambiguous. It means that the callbacks already in
loop._ready at this point will be called before the loop exits, it
doesn't intend to include anything in self._scheduled.

> BaseEventLoop.close() should maybe warn when there are still scheduled
> tasks? Maybe only in debug mode?

I worry that this would be pretty noisy.

> I don't want to document BaseEventLoop._scheduled.

Agreed.

> By the way, the event loop destructor should maybe emit a
> ResourceWarning if the event loop is not closed.

That might be a good idea. But won't you already get one about unclosed sockets?

-- 
--Guido van Rossum (python.org/~guido)

Reply via email to