Skip to content

Commit 89dfbf1

Browse files
committed
gh-112532: Fix memory block count for free-threaded build
This fixes `_PyInterpreterState_GetAllocatedBlocks()` and `_Py_GetGlobalAllocatedBlocks()` in the free-threaded builds. The gh-113263 change that introduced multiple mimalloc heaps per-thread broke the logic for counting the number of allocated blocks. For subtle reasons, this led to reported reference count leaks in the refleaks buildbots.
1 parent ac92527 commit 89dfbf1

File tree

1 file changed

+27
-18
lines changed

1 file changed

+27
-18
lines changed

Objects/obmalloc.c

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -626,12 +626,16 @@ _PyMem_PymallocEnabled(void)
626626
static int
627627
_PyMem_MimallocEnabled(void)
628628
{
629+
#ifdef Py_GIL_DISABLED
630+
return 1;
631+
#else
629632
if (_PyMem_DebugEnabled()) {
630633
return (_PyMem_Debug.obj.alloc.malloc == _PyObject_MiMalloc);
631634
}
632635
else {
633636
return (_PyObject.malloc == _PyObject_MiMalloc);
634637
}
638+
#endif
635639
}
636640
#endif // WITH_MIMALLOC
637641

@@ -1041,20 +1045,35 @@ static bool count_blocks(
10411045
*(size_t *)allocated_blocks += area->used;
10421046
return 1;
10431047
}
1048+
1049+
static Py_ssize_t
1050+
get_mimalloc_allocated_blocks(PyInterpreterState *interp)
1051+
{
1052+
size_t allocated_blocks = 0;
1053+
#ifdef Py_GIL_DISABLED
1054+
for (PyThreadState *t = interp->threads.head; t != NULL; t = t->next) {
1055+
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)t;
1056+
for (int i = 0; i < _Py_MIMALLOC_HEAP_COUNT; i++) {
1057+
mi_heap_t *heap = &tstate->mimalloc.heaps[i];
1058+
mi_heap_visit_blocks(heap, false, &count_blocks, &allocated_blocks);
1059+
}
1060+
}
1061+
// TODO(sgross): count blocks in abandoned segments.
1062+
#else
1063+
// TODO(sgross): this only counts the current thread's blocks.
1064+
mi_heap_t *heap = mi_heap_get_default();
1065+
mi_heap_visit_blocks(heap, false, &count_blocks, &allocated_blocks);
1066+
#endif
1067+
return allocated_blocks;
1068+
}
10441069
#endif
10451070

10461071
Py_ssize_t
10471072
_PyInterpreterState_GetAllocatedBlocks(PyInterpreterState *interp)
10481073
{
10491074
#ifdef WITH_MIMALLOC
1050-
// TODO(sgross): this only counts the current thread's blocks.
10511075
if (_PyMem_MimallocEnabled()) {
1052-
size_t allocated_blocks = 0;
1053-
1054-
mi_heap_t *heap = mi_heap_get_default();
1055-
mi_heap_visit_blocks(heap, false, &count_blocks, &allocated_blocks);
1056-
1057-
return allocated_blocks;
1076+
return get_mimalloc_allocated_blocks(interp);
10581077
}
10591078
#endif
10601079

@@ -1105,7 +1124,7 @@ _PyInterpreterState_FinalizeAllocatedBlocks(PyInterpreterState *interp)
11051124

11061125
static Py_ssize_t get_num_global_allocated_blocks(_PyRuntimeState *);
11071126

1108-
/* We preserve the number of blockss leaked during runtime finalization,
1127+
/* We preserve the number of blocks leaked during runtime finalization,
11091128
so they can be reported if the runtime is initialized again. */
11101129
// XXX We don't lose any information by dropping this,
11111130
// so we should consider doing so.
@@ -1121,16 +1140,6 @@ _Py_FinalizeAllocatedBlocks(_PyRuntimeState *runtime)
11211140
static Py_ssize_t
11221141
get_num_global_allocated_blocks(_PyRuntimeState *runtime)
11231142
{
1124-
#ifdef WITH_MIMALLOC
1125-
if (_PyMem_MimallocEnabled()) {
1126-
size_t allocated_blocks = 0;
1127-
1128-
mi_heap_t *heap = mi_heap_get_default();
1129-
mi_heap_visit_blocks(heap, false, &count_blocks, &allocated_blocks);
1130-
1131-
return allocated_blocks;
1132-
}
1133-
#endif
11341143
Py_ssize_t total = 0;
11351144
if (_PyRuntimeState_GetFinalizing(runtime) != NULL) {
11361145
PyInterpreterState *interp = _PyInterpreterState_Main();

0 commit comments

Comments
 (0)