Skip to content

Conversation

@DinoV
Copy link
Contributor

@DinoV DinoV commented Dec 3, 2025

traverse functions shouldn't modify the ref count during visitation otherwise they'll cause issues in the free threaded GC. CType_Type_traverse uses _PyStgInfo_FromType_NoState to get back to the state object but this causes an incref to happen via PyType_GetBaseByToken.

Instead of using PyType_GetBaseByToken this just walks to find the CType's solid base. Because PyCType_Type adds fields and immediately descends from PyType_Type it will always be the base immediately before PyType_Type.

@encukou
Copy link
Member

encukou commented Dec 4, 2025

traverse functions shouldn't modify the ref count during visitation otherwise they'll cause issues in the free threaded GC.

Is this limitation documented? That sounds like it should be mentioned in the docs, and a free-threading porting guide.

Because PyCType_Type adds fields and immediately descends from PyType_Type it will always be the base immediately before PyType_Type.

There's an assumption I'd like to relax eventually :)
Could we add a _PyType_GetBaseByToken_Borrow instead?
If not, please add an assertion that the token matches.

Copy link
Contributor

@colesbury colesbury left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this found from a crash or by inspecting the code? Either way, I think it's worth fixing, but my understanding was that the problematic pattern involved incref/decref around the visit, i.e. something like:

Py_INCREF(obj);
Py_VISIT(obj);
Py_DECREF(obj);

While this code was more like:


Py_INCREF(obj);
Py_DECREF(obj);
Py_VISIT(obj);

I think we can make the free threaded GC less susceptible to this problem, but we should also document the things you shouldn't do in a tp_traverse handler.

Comment on lines 622 to 625
if (PyCType_Type == NULL) {
PyErr_Format(PyExc_TypeError, "expected a ctypes type, got '%N'", type);
return NULL;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't create exceptions (or any other object) during tp_traverse handlers either. I think this should be pulled up to the tp_dealloc / tp_clear if we want to print unraisable exceptions there.

@mpage
Copy link
Contributor

mpage commented Dec 4, 2025

Was this found from a crash or by inspecting the code?

I found this from a crash that occurred while porting the parallel GC in cinder (it can call the same traverse function from multiple threads) to 3.14.

@DinoV DinoV force-pushed the dont_modify_refcount branch from 346043e to f860118 Compare December 5, 2025 19:18
@DinoV DinoV requested a review from markshannon as a code owner December 5, 2025 19:18
@DinoV
Copy link
Contributor Author

DinoV commented Dec 5, 2025

There's an assumption I'd like to relax eventually :) Could we add a _PyType_GetBaseByToken_Borrow instead? If not, please add an assertion that the token matches.

Sounds good, I've gone ahead and added the Borrow version and switched to using it instead.

@encukou
Copy link
Member

encukou commented Dec 10, 2025

Thanks! Looks great; I left some nitpicks in DinoV#2

@encukou encukou merged commit da8199f into python:main Dec 11, 2025
46 checks passed
@miss-islington-app
Copy link

Thanks @DinoV for the PR, and @encukou for merging it 🌮🎉.. I'm working now to backport this PR to: 3.14.
🐍🍒⛏🤖

@miss-islington-app
Copy link

Sorry, @DinoV and @encukou, I could not cleanly backport this to 3.14 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker da8199f8842c2830fa4015138725849863523de4 3.14

@encukou
Copy link
Member

encukou commented Dec 11, 2025

Thank you!

encukou pushed a commit to encukou/cpython that referenced this pull request Dec 11, 2025
@bedevere-app
Copy link

bedevere-app bot commented Dec 11, 2025

GH-142567 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.14 bugs and security fixes label Dec 11, 2025
DinoV added a commit that referenced this pull request Dec 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants