-
-
Notifications
You must be signed in to change notification settings - Fork 33.1k
gh-138791: fix crash when visiting a non-consumed timer handler #138789
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
Conversation
5f568a8
to
3873fd5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that the point of this extra reference is that TkttObject should not be collected until the timer fires. You call createtimerhandler()
, do not save the result, and it should live until the timer function is invoked, then it can be collected.
So, it cannot be cleared if self->func
is not NULL, and if self->func
is NULL, there is no longer an extra reference.
If self->func
is not NULL, just leave Tktt_Traverse()
.
Oh maybe here is what I should do:
|
Dealloc() is only called when the refcount is 0. This cannot happen before the timer was triggered. Clear() should not clear the function before it was invoked. When it has been invoked, nothing to clear. The loop created by TkttObject himself should not be broken. But we want to break larger loops which contain a TkttObject (TkttObject -> func -> ... ->TkttObject). And it would be nice if Hmm. Then we should not touch Tktt_Traverse, but make Tktt_Clear a no-op. Or just set |
But how would you ensure that the following doesn't leak at the end of the execution: root = tkinter.Tk()
func = lambda *_, **__: None
func.evil = type(root.tk.createtimerhandler(10000, print))
root.tk.createtimerhandler(10000, func)
root.destroy() Note that the following crashes for me: root = tkinter.Tk()
func = lambda *_, **__: None
func.evil = type(root.tk.createtimerhandler(10000, print))
root.tk.createtimerhandler(10000, func)
root.mainloop() If after executing the code, close the window and close the REPL, I get (on main): python: ./Modules/_tkinter.c:2784: int Tktt_Traverse(PyObject *, visitproc, void *): Assertion `TkttObject_Check(op)' failed. |
TkttObject *self = TkttObject_CAST(op); | ||
Py_VISIT(Py_TYPE(op)); | ||
if (self->token != NULL) { | ||
Py_VISIT(op); /* See Tktt_New() */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be missing something, but won't this just lead to infinite recursion?
} | ||
if (self->func != NULL) { | ||
Py_CLEAR(self->func); | ||
Py_DECREF(op); /* See Tktt_New() */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I'm probably missing something -- why do we have that extra reference in the first place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To prevent the result of createtimerhandler()
from being collected before the timer fires.
I tried different options, but I couldn't get rid of the crash. #138331 should be reverted. |
Mmh, yes, if adding the GC causes this, let's revert this part. I don't think we need to revert the GC support on TkAppObject though. |
Please test that reverting only TkttObject helps. |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
I'll work on this one during the sprint and tomorrow, but offline. I'll need to think about how to prevent leaks and crashes while keeping the object alive. |
Shall we start making use of the |
I wanted to make it a draft (I'm on mobile only). And I think we can use the sprint label yes |
I'm going to close this one until I've decided what to do with the GC for Tk. I don't think it's something I can fix in a matter of hours. |
In 283380a, I forgot to take into account the following:
When the handler is called, this extra reference is cleared and the Tcl handler is destroyed. However, if the object is destroyed before that, this never happens, so we're having a leak.
cc @serhiy-storchaka