Skip to content

Commit dad8a8d

Browse files
committed
Adapt the free-threaded build to mimalloc v2.5.2
mimalloc v2.1.4 replaced the list of abandoned segments with per-arena bitmaps and introduced sub-processes (mi_subproc_t) to keep abandoned segments of separate interpreters apart. Use them instead of the CPython-specific mi_abandoned_pool_t: - Each interpreter owns a mi_subproc_t created in init_interpreter(). It is never deleted because abandoned segments may still reference it after the interpreter is gone. - tstate_mimalloc_bind() sets tld->segments.subproc and uses the upstream _mi_heap_init() signature. - The GC and sys.getallocatedblocks() visit abandoned segments with _mi_abandoned_visit_blocks_unlocked() while STW.
1 parent fa3a143 commit dad8a8d

4 files changed

Lines changed: 27 additions & 18 deletions

File tree

‎Include/internal/pycore_mimalloc.h‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ extern "C++" {
5252

5353
#ifdef Py_GIL_DISABLED
5454
struct _mimalloc_interp_state {
55-
// When exiting, threads place any segments with live blocks in this
56-
// shared pool for other threads to claim and reuse.
57-
mi_abandoned_pool_t abandoned_pool;
55+
// When exiting, threads abandon any segments with live blocks. The
56+
// abandoned segments are tracked per mimalloc sub-process so that other
57+
// threads of the same interpreter can claim and reuse them, and so that
58+
// the GC can visit them.
59+
mi_subproc_t *subproc;
5860
};
5961

6062
struct _mimalloc_thread_state {

‎Objects/obmalloc.c‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,10 +1823,10 @@ get_mimalloc_allocated_blocks(PyInterpreterState *interp)
18231823
}
18241824
}
18251825

1826-
mi_abandoned_pool_t *pool = &interp->mimalloc.abandoned_pool;
1827-
for (uint8_t tag = 0; tag < _Py_MIMALLOC_HEAP_COUNT; tag++) {
1828-
_mi_abandoned_pool_visit_blocks(pool, tag, false, &count_blocks,
1829-
&allocated_blocks);
1826+
mi_subproc_t *subproc = interp->mimalloc.subproc;
1827+
for (int tag = 0; tag < _Py_MIMALLOC_HEAP_COUNT; tag++) {
1828+
_mi_abandoned_visit_blocks_unlocked(subproc, tag, false, &count_blocks,
1829+
&allocated_blocks);
18301830
}
18311831
#else
18321832
// TODO(sgross): this only counts the current thread's blocks.

‎Python/gc_free_threading.c‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -399,16 +399,17 @@ gc_visit_heaps_lock_held(PyInterpreterState *interp, mi_block_visit_fun *visitor
399399
}
400400
}
401401

402-
// visit blocks in the per-interpreter abandoned pool (from dead threads)
403-
mi_abandoned_pool_t *pool = &interp->mimalloc.abandoned_pool;
402+
// visit blocks in abandoned segments (from dead threads) of the
403+
// per-interpreter mimalloc sub-process.
404+
mi_subproc_t *subproc = interp->mimalloc.subproc;
404405
arg->offset = offset_base;
405-
if (!_mi_abandoned_pool_visit_blocks(pool, _Py_MIMALLOC_HEAP_GC, true,
406-
visitor, arg)) {
406+
if (!_mi_abandoned_visit_blocks_unlocked(subproc, _Py_MIMALLOC_HEAP_GC,
407+
true, visitor, arg)) {
407408
return -1;
408409
}
409410
arg->offset = offset_pre;
410-
if (!_mi_abandoned_pool_visit_blocks(pool, _Py_MIMALLOC_HEAP_GC_PRE, true,
411-
visitor, arg)) {
411+
if (!_mi_abandoned_visit_blocks_unlocked(subproc, _Py_MIMALLOC_HEAP_GC_PRE,
412+
true, visitor, arg)) {
412413
return -1;
413414
}
414415
return 0;

‎Python/pystate.c‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,11 @@ init_interpreter(PyInterpreterState *interp,
573573
PyConfig_InitPythonConfig(&interp->config);
574574
#ifdef Py_GIL_DISABLED
575575
_Py_brc_init_state(interp);
576+
577+
interp->mimalloc.subproc = mi_subproc_new();
578+
if (interp->mimalloc.subproc == NULL) {
579+
return _PyStatus_NO_MEMORY();
580+
}
576581
#endif
577582

578583
llist_init(&interp->mem_free_queue.head);
@@ -3325,10 +3330,11 @@ tstate_mimalloc_bind(PyThreadState *tstate)
33253330
_mi_tld_init(tld, &mts->heaps[_Py_MIMALLOC_HEAP_MEM]);
33263331
llist_init(&mts->page_list);
33273332

3328-
// Exiting threads push any remaining in-use segments to the abandoned
3329-
// pool to be re-claimed later by other threads. We use per-interpreter
3330-
// pools to keep Python objects from different interpreters separate.
3331-
tld->segments.abandoned = &tstate->interp->mimalloc.abandoned_pool;
3333+
// Exiting threads abandon any remaining in-use segments to be re-claimed
3334+
// later by other threads. We use a per-interpreter mimalloc sub-process
3335+
// to keep Python objects from different interpreters separate.
3336+
assert(tstate->interp->mimalloc.subproc != NULL);
3337+
tld->segments.subproc = tstate->interp->mimalloc.subproc;
33323338

33333339
// Don't fill in the first N bytes up to ob_type in debug builds. We may
33343340
// access ob_tid and the refcount fields in the dict and list lock-less
@@ -3346,7 +3352,7 @@ tstate_mimalloc_bind(PyThreadState *tstate)
33463352

33473353
// Initialize each heap
33483354
for (uint8_t i = 0; i < _Py_MIMALLOC_HEAP_COUNT; i++) {
3349-
_mi_heap_init_ex(&mts->heaps[i], tld, _mi_arena_id_none(), false, i);
3355+
_mi_heap_init(&mts->heaps[i], tld, _mi_arena_id_none(), false, i);
33503356
mts->heaps[i].debug_offset = (uint8_t)debug_offsets[i];
33513357
}
33523358

0 commit comments

Comments
 (0)