[Async-sig] Asynchronous cleanup is a problem
Nathaniel Smith
njs at pobox.com
Tue Jul 5 20:56:50 EDT 2016
So here's an interesting issue I just discovered while experimenting
with async generators. It caught me by surprise, anyway. Maybe this
was already obvious to everyone else. But I wanted to get some more
perspectives.
Starting axiom: async functions / async generators need to be prepared
for the case where they get garbage collected before being run to
completion, because, well... this is a thing that can happen.
Currently, the way garbage collection is handled is that their __del__
method calls their .close() method, which does something like:
class GeneratorType:
...
def close(self):
try:
self.throw(GeneratorExit)
except GeneratorExit, StopIteration:
return # it worked, all is good
except:
raise # double-fault, propagate
else:
raise RuntimeError("generator ignored GeneratorExit")
(see PEP 342).
So far, so obvious -- an async function that gets a GeneratorExit has
to propagate that exception immediately. This is a regular method, not
an async method, because it
Dismaying conclusion: inside an async function / async generator,
finally: blocks must never yield (and neither can __aexit__
callbacks), because they might be invoked during garbage collection.
For async functions this is... arguably a problem but not super
urgent, because async functions rarely get garbage collected without
being run to completion. For async generators it's a much bigger
problem.
There's some more discussion, and a first sketch at conventions we
might want to use for handling this, here:
https://github.com/dabeaz/curio/issues/70
-n
--
Nathaniel J. Smith -- https://vorpus.org
More information about the Async-sig
mailing list