Skip to content

Commit

Permalink
Improved README.
Browse files Browse the repository at this point in the history
  • Loading branch information
cjrh committed Nov 7, 2017
1 parent a3d4f7b commit cd9771d
Showing 1 changed file with 23 additions and 21 deletions.
44 changes: 23 additions & 21 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ idiomatic actions for asyncio apps:
- calls ``loop.run_forever()``,
- adds default (and smart) signal handlers for both ``SIGINT``
and ``SIGTERM`` that will stop the loop;
- and when the loop stops, the it will...
- and *when* the loop stops (either by signal or called directly), then it will...
- ...gather all outstanding tasks,
- cancel them using ``task.cancel()``,
- wait for the executor to complete shutdown, and
- resume running the loop until all those tasks are done,
- wait for the *executor* to complete shutdown, and
- finally close the loop.

All of this stuff is boilerplate that you will never have to write
Expand All @@ -69,10 +70,12 @@ again. So, if you use ``aiorun`` this is what **you** need to remember:
- Spawn all your work from a single, starting coroutine
- When a shutdown signal is received, **all** currently-pending tasks
will have ``CancelledError`` raised internally. It's up to you whether
you want to handle this (inside your coroutines) with
you want to handle this inside each coroutine with
a ``try/except`` or not.
- Try to have executor jobs be shortish, since shutdown will wait for them
to finish. If you need a long-running thread or process tasks, use
- If you want to protect coros from cancellation, see `shutdown_waits_for()`
further down.
- Try to have executor jobs be shortish, since the shutdown process will wait
for them to finish. If you need a long-running thread or process tasks, use
a dedicated thread/subprocess and set ``daemon=True`` instead.

There's not much else to know for general use. `aiorun` has a few special
Expand All @@ -86,23 +89,23 @@ It's unusual, but sometimes you're going to want a coroutine to not get
interrupted by cancellation *during the shutdown sequence*. You'll look in
the official docs and find ``asyncio.shield()``.

The problem is that ``shield()`` doesn't work in shutdown scenarios because
Unfortunately, ``shield()`` doesn't work in shutdown scenarios because
the protection offered by ``shield()`` only applies if the specific coroutine
*inside which* the ``shield()`` is used, gets cancelled directly.

If, however, you go through a conventional shutdown sequence (like ``aiorun``
is doing internally), you would call:
Let me explain: if you do a conventional shutdown sequence (like ``aiorun``
is doing internally), this is the sequence of steps:

- ``tasks = all_tasks()``, followed by
- ``group = gather(*tasks)``, and then
- ``group.cancel()``

The problem is that ```shield()`` creates a *secret, inner*
task that will also be captured in the ``all_tasks()`` call above, so it
will also receive a cancellation signal just like everything else.
The way ``shield()`` works internally is it creates a *secret, inner*
task—which also gets included in the ``all_tasks()`` call above! Thus
it also receives a cancellation signal just like everything else.

Therefore, we have a version of ``shield()`` that works better for us:
``shutdown_waits_for()``. If you've got a coroutine that must **not** be
Therefore, we have an alternative version of ``shield()`` that works better for
us: ``shutdown_waits_for()``. If you've got a coroutine that must **not** be
cancelled during the shutdown sequence, just wrap it in
``shutdown_waits_for()``!

Expand All @@ -125,20 +128,19 @@ Here's an example:
run(main())
If you run this program and do nothing, it'll run forever ('cause that's
how ``aiorun.run()`` works) and you'll see only ``done!`` printed in the output.
You'll have to send a signal or ``CTRL-C`` to stop it, at which point
you'll see ``oh noes!`` printed. So far no surprises.

If, however, you hit ``CTRL-C`` *before* 60 seconds has passed, you will see
If you hit ``CTRL-C`` *before* 60 seconds has passed, you will see
``oh noes!`` printed immediately, and then after 60 seconds (since start),
``done!`` is printed, and thereafter the program exits.

Behind the scenes, ``all tasks()`` would have been cancelled by ``CTRL-C``,
except ones wrapped in ``shutdown_waits_for()`` calls. In this respect, it
Behind the scenes, ``all_tasks()`` would have been cancelled by ``CTRL-C``,
*except* ones wrapped in ``shutdown_waits_for()`` calls. In this respect, it
is loosely similar to ``asyncio.shield()``, but with special applicability
to our shutdown scenario in ``aiorun()``.

Be careful with this: the coroutine should still finish up at some point.
The main use case for this is short-lived tasks that you don't want to
write explicit cancellation handling.

Oh, and you can use ``shutdown_waits_for()`` as if it were ``asyncio.shield()``
too. For that use-case it works the same. If you're using ``aiorun``, there
is no reason to use ``shield()``.

0 comments on commit cd9771d

Please sign in to comment.