Resolve the free-threaded c_stack_refs offset at runtime - #528
Conversation
CPython 3.14.4 appended datastack_cached_chunk to PyThreadState, moving _PyThreadStateImpl.c_stack_refs from offset 912 to 920 within the 3.14 series. One cp314t wheel serves every 3.14.x, so a wheel built for 3.14.4 or later read 8 bytes past the field on 3.14.0 through 3.14.3 and landed on gc.alloc_count. 3.5.3 only copied that word around; 3.5.4 walks it as a list, so it segfaults on the first switch. Locate the field at import instead of trusting offsetof(). Reading back a data descriptor puts us inside _PyObject_GenericGetAttrWithDict, which has the real layout and holds a _PyCStackRef to that descriptor while it calls __get__; the word of the thread state pointing at that node is c_stack_refs. The search is bounded by two of our own stack markers and matched against the descriptor. If it comes up empty on an interpreter we were not built for, import fails instead of the first switch. Tested on 3.14.6t, 3.15.0b2t, 3.14.6 and 3.13.14, plus wheels built for 3.14.6 run on 3.14.3 and the reverse. Fixes python-greenlet#527
|
I very much appreciate the effort, but I'm disinclined to merge this. I don't like all the assumptions and mucking around used to try to figure out the offset. I think the solution is much simpler: If you're using a binary wheel and it's crashing, either upgrade your Python interpreter, or build greenlet from source. 3.14.4 was released back in April and we're up to 3.14.7 now, so there are more releases that are compatible with the built wheels released yesterday than there are incompatible and over time that number will only continue to go up. So I don't see much value in catering to a shrinking userbase with a significant chunk of complex code when the solutions of either upgrading your interpreter or building from source are so easy. |
|
Makes sense, I agree. |
Fixes #527