Skip to content

Commit 31633f4

Browse files
authored
gh-115184: Fix refleak tracking issues in free-threaded build (#115188)
Fixes a few issues related to refleak tracking in the free-threaded build: - Count blocks in abandoned segments - Call `_mi_page_free_collect` earlier during heap traversal in order to get an accurate count of blocks in use. - Add missing refcount tracking in `_Py_DecRefSharedDebug` and `_Py_ExplicitMergeRefcount`. - Pause threads in `get_num_global_allocated_blocks` to ensure that traversing the mimalloc heaps is safe.
1 parent 769d444 commit 31633f4

File tree

3 files changed

+16
-6
lines changed

3 files changed

+16
-6
lines changed

Objects/mimalloc/heap.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,6 @@ bool _mi_heap_area_visit_blocks(const mi_heap_area_t* area, mi_page_t *page, mi_
538538
mi_assert(page != NULL);
539539
if (page == NULL) return true;
540540

541-
_mi_page_free_collect(page,true);
542541
mi_assert_internal(page->local_free == NULL);
543542
if (page->used == 0) return true;
544543

@@ -635,6 +634,7 @@ bool _mi_heap_area_visit_blocks(const mi_heap_area_t* area, mi_page_t *page, mi_
635634
typedef bool (mi_heap_area_visit_fun)(const mi_heap_t* heap, const mi_heap_area_ex_t* area, void* arg);
636635

637636
void _mi_heap_area_init(mi_heap_area_t* area, mi_page_t* page) {
637+
_mi_page_free_collect(page,true);
638638
const size_t bsize = mi_page_block_size(page);
639639
const size_t ubsize = mi_page_usable_block_size(page);
640640
area->reserved = page->reserved * bsize;

Objects/object.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,9 @@ _Py_DecRefSharedDebug(PyObject *o, const char *filename, int lineno)
346346
if (should_queue) {
347347
// TODO: the inter-thread queue is not yet implemented. For now,
348348
// we just merge the refcount here.
349+
#ifdef Py_REF_DEBUG
350+
_Py_IncRefTotal(_PyInterpreterState_GET());
351+
#endif
349352
Py_ssize_t refcount = _Py_ExplicitMergeRefcount(o, -1);
350353
if (refcount == 0) {
351354
_Py_Dealloc(o);
@@ -399,17 +402,17 @@ _Py_ExplicitMergeRefcount(PyObject *op, Py_ssize_t extra)
399402
Py_ssize_t shared = _Py_atomic_load_ssize_relaxed(&op->ob_ref_shared);
400403
do {
401404
refcnt = Py_ARITHMETIC_RIGHT_SHIFT(Py_ssize_t, shared, _Py_REF_SHARED_SHIFT);
402-
if (_Py_REF_IS_MERGED(shared)) {
403-
return refcnt;
404-
}
405-
406405
refcnt += (Py_ssize_t)op->ob_ref_local;
407406
refcnt += extra;
408407

409408
new_shared = _Py_REF_SHARED(refcnt, _Py_REF_MERGED);
410409
} while (!_Py_atomic_compare_exchange_ssize(&op->ob_ref_shared,
411410
&shared, new_shared));
412411

412+
#ifdef Py_REF_DEBUG
413+
_Py_AddRefTotal(_PyInterpreterState_GET(), extra);
414+
#endif
415+
413416
_Py_atomic_store_uint32_relaxed(&op->ob_ref_local, 0);
414417
_Py_atomic_store_uintptr_relaxed(&op->ob_tid, 0);
415418
return refcnt;

Objects/obmalloc.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1073,7 +1073,12 @@ get_mimalloc_allocated_blocks(PyInterpreterState *interp)
10731073
mi_heap_visit_blocks(heap, false, &count_blocks, &allocated_blocks);
10741074
}
10751075
}
1076-
// TODO(sgross): count blocks in abandoned segments.
1076+
1077+
mi_abandoned_pool_t *pool = &interp->mimalloc.abandoned_pool;
1078+
for (uint8_t tag = 0; tag < _Py_MIMALLOC_HEAP_COUNT; tag++) {
1079+
_mi_abandoned_pool_visit_blocks(pool, tag, false, &count_blocks,
1080+
&allocated_blocks);
1081+
}
10771082
#else
10781083
// TODO(sgross): this only counts the current thread's blocks.
10791084
mi_heap_t *heap = mi_heap_get_default();
@@ -1189,6 +1194,7 @@ get_num_global_allocated_blocks(_PyRuntimeState *runtime)
11891194
}
11901195
}
11911196
else {
1197+
_PyEval_StopTheWorldAll(&_PyRuntime);
11921198
HEAD_LOCK(runtime);
11931199
PyInterpreterState *interp = PyInterpreterState_Head();
11941200
assert(interp != NULL);
@@ -1208,6 +1214,7 @@ get_num_global_allocated_blocks(_PyRuntimeState *runtime)
12081214
}
12091215
}
12101216
HEAD_UNLOCK(runtime);
1217+
_PyEval_StartTheWorldAll(&_PyRuntime);
12111218
#ifdef Py_DEBUG
12121219
assert(got_main);
12131220
#endif

0 commit comments

Comments
 (0)