-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
Crash in asyncio when printing a task object at teardown #96232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
CC: @graingert |
Backtrace of the crash:
In debug mode we get an assert failing:
And here is the problem already-cleaned object:
|
This is enough to fix the segfault, but is a very incomplete fix: diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 1f6de2177a..0be5ca1e8c 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -1396,6 +1396,10 @@ static PyObject *
_asyncio_Future__repr_info_impl(FutureObj *self)
/*[clinic end generated code: output=fa69e901bd176cfb input=f21504d8e2ae1ca2]*/
{
+ if (!asyncio_future_repr_info_func) {
+ PyErr_SetString(PyExc_RuntimeError, "Printing a _asyncio.Future object at shutdown");
+ return NULL;
+ }
return PyObject_CallOneArg(asyncio_future_repr_info_func, (PyObject *)self);
} Technically the crash can happen with any method. |
Would be great if someone can work on a simple reproducer. We need a task/future object that survives the first collection at shutdown and something tries to print it in the second. In the reproducer, what tries to remove it is this code:
that |
CC: @kumaraditya303 in case you want a challenge :) |
I started messing around triggering Task GC in this reproducer, I'm not sure if it will help here |
@pablogsal Which exact Python version and commit were you using? (The line numbers in the stack trace seem slightly off.) Maybe this was already obvious to everybody on this issue, but from the stack trace it looks like a bunch of nested There are several It would seem that something is finally cancelling this stack of I also note that the message pump code uses a for task in self._child_tasks:
task.cancel()
await task
self._child_tasks.clear() Would it be possible that this loop doesn't cancel all the tasks because some tasks are GC'ed elsewhere? We know that keeping a weak reference to a task is not enough to keep it alive, GC can kill it without closing it. This mechanism is only used for timers created by Someone should probably investigate that timer class more (since there may be a clue to a simpler repro here), but I'm tired and I need to go to bed. |
Oh darn, looks like I was looking at the HEAD of the main branch of textualize, instead of the commit 5fe47da058564b72e5081a5658c054f55a5d85cd as Pablo said. The |
I still haven't managed to create a smaller reproducer but hopefully this will be fixed once #91375 is fixed. |
PR #99122 is merged now so this shouldn't happen on 3.12+. Since nobody was able to create a reproducer for this, I propose to close this as won't fix. |
Agreed. |
If a task object is still alive at interpreter teardown, something can try to print it. This will end in
_asyncio.Future._repr_info
that in turn calls:but
asyncio_future_repr_info_func
at that point can beNULL
becausemodule_free
of_asynciomodule.c
has been executed already, called byPy_FinalizeEx -> finalize_modules -> _PyInterpreterState_ClearModules -> ... -> module_dealloc
.The crash happens in the last
_PyGC_CollectNoFail
offinalize_modules
so somehow the task survived the first collection and then was garbage in the second one.I don;t have a simple repro for now, but you can get the failure by cloning https://github.com/Textualize/textual checking commit 5fe47da058564b72e5081a5658c054f55a5d85cd, installing the project and test deps and running
pytest
.The text was updated successfully, but these errors were encountered: