Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Remove unneeded 'mutex' member.
  • Loading branch information
nascheme committed May 5, 2026
commit 6a12aef2fdee16aefa77c309e2d9f0e98685edde
1 change: 1 addition & 0 deletions Include/internal/pycore_gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ extern int _PyGC_VisitStackRef(union _PyStackRef *ref, visitproc visit, void *ar
#ifdef Py_GIL_DISABLED
extern void _PyGC_VisitObjectsWorldStopped(PyInterpreterState *interp,
gcvisitobjects_t callback, void *arg);
extern Py_ssize_t _PyGC_GetMimallocAllocatedBytes(PyInterpreterState *interp);
#endif

#ifdef __cplusplus
Expand Down
3 changes: 0 additions & 3 deletions Include/internal/pycore_interp_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,6 @@ struct _gc_runtime_state {
Adjusted after each collection based on the fraction of objects found to
be trash. */
int adaptive_threshold;

/* Mutex held for gc_should_collect_mem_usage(). */
PyMutex mutex;
#else
PyGC_Head *generation0;
#endif
Expand Down
41 changes: 41 additions & 0 deletions Python/gc_free_threading.c
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,47 @@ gc_visit_heaps(PyInterpreterState *interp, mi_block_visit_fun *visitor,
return err;
}

// Visitor for _PyGC_GetMimallocAllocatedBytes(): called once per heap area
// when visit_blocks=false. Sums area->used * area->block_size.
static bool
mimalloc_used_area_visitor(const mi_heap_t *heap, const mi_heap_area_t *area,
void *block, size_t block_size, void *arg)
{
if (block == NULL) {
*(Py_ssize_t *)arg += (Py_ssize_t)(area->used * area->block_size);
}
return true;
}

// Return the total bytes in use across all mimalloc heaps for all threads in
// the interpreter, plus the per-interp abandoned pool.
Py_ssize_t
_PyGC_GetMimallocAllocatedBytes(PyInterpreterState *interp)
{
Py_ssize_t total = 0;
_PyEval_StopTheWorld(interp);
HEAD_LOCK(&_PyRuntime);
_Py_FOR_EACH_TSTATE_UNLOCKED(interp, p) {
struct _mimalloc_thread_state *m =
&((_PyThreadStateImpl *)p)->mimalloc;
if (!_Py_atomic_load_int(&m->initialized)) {
continue;
}
for (int h = 0; h < _Py_MIMALLOC_HEAP_COUNT; h++) {
mi_heap_visit_blocks(&m->heaps[h], false,
mimalloc_used_area_visitor, &total);
}
}
mi_abandoned_pool_t *pool = &interp->mimalloc.abandoned_pool;
for (uint8_t tag = 0; tag < _Py_MIMALLOC_HEAP_COUNT; tag++) {
_mi_abandoned_pool_visit_blocks(pool, tag, false,
mimalloc_used_area_visitor, &total);
}
HEAD_UNLOCK(&_PyRuntime);
_PyEval_StartTheWorld(interp);
return total;
}

static inline void
gc_visit_stackref(_PyStackRef stackref)
{
Expand Down
29 changes: 29 additions & 0 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Data members:
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_ceval.h" // _PyEval_SetAsyncGenFinalizer()
#include "pycore_frame.h" // _PyInterpreterFrame
#include "pycore_gc.h" // _PyGC_GetMimallocAllocatedBytes()
#include "pycore_import.h" // _PyImport_SetDLOpenFlags()
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
#include "pycore_interpframe.h" // _PyFrame_GetFirstComplete()
Expand Down Expand Up @@ -2060,6 +2061,32 @@ sys_getallocatedblocks_impl(PyObject *module)
return _Py_GetGlobalAllocatedBlocks();
}

PyDoc_STRVAR(sys__get_mimalloc_allocated_bytes__doc__,
"_get_mimalloc_allocated_bytes($module, /)\n"
"--\n"
"\n"
"Return total bytes allocated across all mimalloc heaps in this interpreter.\n"
"\n"
"Free-threaded build only. Stops the world while reading per-thread heap\n"
"structures. Intended for benchmarking: the OS RSS does not reliably reflect\n"
"Python's live memory because mimalloc retains freed pages.\n"
"Raises NotImplementedError on the GIL-enabled build.");

static PyObject *
sys__get_mimalloc_allocated_bytes(PyObject *module, PyObject *Py_UNUSED(ignored))
{
#ifdef Py_GIL_DISABLED
PyInterpreterState *interp = _PyInterpreterState_GET();
Py_ssize_t total = _PyGC_GetMimallocAllocatedBytes(interp);
return PyLong_FromSsize_t(total);
#else
PyErr_SetString(PyExc_NotImplementedError,
"sys._get_mimalloc_allocated_bytes() is only available "
"on the free-threaded build");
return NULL;
#endif
}

/*[clinic input]
sys.getunicodeinternedsize -> Py_ssize_t

Expand Down Expand Up @@ -2927,6 +2954,8 @@ static PyMethodDef sys_methods[] = {
SYS_GETDEFAULTENCODING_METHODDEF
SYS_GETDLOPENFLAGS_METHODDEF
SYS_GETALLOCATEDBLOCKS_METHODDEF
{"_get_mimalloc_allocated_bytes", sys__get_mimalloc_allocated_bytes,
METH_NOARGS, sys__get_mimalloc_allocated_bytes__doc__},
SYS_GETUNICODEINTERNEDSIZE_METHODDEF
SYS_GETFILESYSTEMENCODING_METHODDEF
SYS_GETFILESYSTEMENCODEERRORS_METHODDEF
Expand Down