-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
gh-112529: Use GC heaps for GC allocations in free-threaded builds #114157
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
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
#ifndef Py_INTERNAL_OBJECT_ALLOC_H | ||
#define Py_INTERNAL_OBJECT_ALLOC_H | ||
|
||
#include "pycore_object.h" // _PyType_HasFeature() | ||
#include "pycore_pystate.h" // _PyThreadState_GET() | ||
#include "pycore_tstate.h" // _PyThreadStateImpl | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
#ifndef Py_BUILD_CORE | ||
# error "this header requires Py_BUILD_CORE define" | ||
#endif | ||
|
||
#ifdef Py_GIL_DISABLED | ||
static inline mi_heap_t * | ||
_PyObject_GetAllocationHeap(_PyThreadStateImpl *tstate, PyTypeObject *tp) | ||
{ | ||
struct _mimalloc_thread_state *m = &tstate->mimalloc; | ||
if (_PyType_HasFeature(tp, Py_TPFLAGS_PREHEADER)) { | ||
return &m->heaps[_Py_MIMALLOC_HEAP_GC_PRE]; | ||
} | ||
else if (_PyType_IS_GC(tp)) { | ||
return &m->heaps[_Py_MIMALLOC_HEAP_GC]; | ||
} | ||
else { | ||
return &m->heaps[_Py_MIMALLOC_HEAP_OBJECT]; | ||
} | ||
} | ||
#endif | ||
|
||
// Sets the heap used for PyObject_Malloc(), PyObject_Realloc(), etc. calls in | ||
// Py_GIL_DISABLED builds. We use different heaps depending on if the object | ||
// supports GC and if it has a pre-header. We smuggle the choice of heap | ||
// through the _mimalloc_thread_state. In the default build, this simply | ||
// calls PyObject_Malloc(). | ||
static inline void * | ||
_PyObject_MallocWithType(PyTypeObject *tp, size_t size) | ||
{ | ||
#ifdef Py_GIL_DISABLED | ||
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET(); | ||
struct _mimalloc_thread_state *m = &tstate->mimalloc; | ||
m->current_object_heap = _PyObject_GetAllocationHeap(tstate, tp); | ||
#endif | ||
void *mem = PyObject_Malloc(size); | ||
#ifdef Py_GIL_DISABLED | ||
m->current_object_heap = &m->heaps[_Py_MIMALLOC_HEAP_OBJECT]; | ||
#endif | ||
return mem; | ||
} | ||
|
||
static inline void * | ||
_PyObject_ReallocWithType(PyTypeObject *tp, void *ptr, size_t size) | ||
{ | ||
#ifdef Py_GIL_DISABLED | ||
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET(); | ||
struct _mimalloc_thread_state *m = &tstate->mimalloc; | ||
m->current_object_heap = _PyObject_GetAllocationHeap(tstate, tp); | ||
#endif | ||
void *mem = PyObject_Realloc(ptr, size); | ||
#ifdef Py_GIL_DISABLED | ||
m->current_object_heap = &m->heaps[_Py_MIMALLOC_HEAP_OBJECT]; | ||
#endif | ||
return mem; | ||
} | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
#endif // !Py_INTERNAL_OBJECT_ALLOC_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
It seems like this shouldn't be strictly necessary? We should always be going through this API to allocate otherwise we might get the wrong heap. We could keep this NULL in debug builds and assert that it's not NULL when we hit the allocator.
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.
You can call
PyObject_Malloc()
directly -- that doesn't go through this API. Only calls that might allocate GC objects need to go through this. So we have to restorecurrent_object_heap
to the non-GC object heap.You could structure this a bit differently and have
PyObject_Malloc()
also go through this sort of thing, but I think that would end up being more complicated.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.
But if you call
PyObject_Malloc
directly aren't things now broken with the pre-header? It seems thatPy_TPFLAGS_MANAGED_DICT
andPy_TPFLAGS_MANAGED_WEAKREF
are both documented as being available to extension objects which would be the primary callers ofPyObject_Malloc
. Maybe we at least need to document that if you're using these flags you need to callPyType_GenericAlloc
inDoc/c-api/typeobj.rst
.I suppose before this you'd be probably broken if you called
PyObject_Malloc
, but it would have crashed and burned pretty obviously, and you could figure out to call_PyType_PreHeaderSize
. Now it seems critical that you callPyType_GenericAlloc
or_PyObject_MallocWithType
or you'll get really subtle crashes in multi-threaded scenarios.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.
You can call
PyObject_Malloc()
for non-GC objects, but not for GC-objects. GC objects have had "pre-headers" (PyGC_Head
) beforePy_TPFLAGS_MANAGED_DICT
was introduced.These restrictions have been in place since Python 2.2a3:
https://github.com/python/cpython/blob/72abb8c5d487ead9eb115fec8132ccef5ba189e5/Misc/HISTORY#L24391-L24398
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.
Ahh, true, I hadn't considered that the pre-header implies GC, that makes sense!