Skip to content

Commit

Permalink
Exclude tlbc from refleak counts
Browse files Browse the repository at this point in the history
TLBC is intended to be reused across threads with different lifetimes,
so may appear as a "leak" depending on the order in which threads execute
code objects. They are freed when the code object is destroyed, which
typically occurs when the runtime is finalized.
  • Loading branch information
mpage committed Sep 27, 2024
1 parent 7dfd1ca commit 7c9da24
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Lib/test/libregrtest/refleak.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ def get_pooled_int(value):
# Use an internal-only keyword argument that mypy doesn't know yet
_only_immortal=True) # type: ignore[call-arg]
alloc_after = getallocatedblocks() - interned_immortal_after
if support.Py_GIL_DISABLED:
# Ignore any thread-local bytecode that was allocated. These will be
# released when the code object is destroyed, typically at runtime
# shutdown
import _testinternalcapi
alloc_after -= _testinternalcapi.get_tlbc_blocks()
rc_after = gettotalrefcount()
fd_after = fd_count()

Expand Down
28 changes: 28 additions & 0 deletions Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -2006,6 +2006,33 @@ get_tlbc_id(PyObject *Py_UNUSED(module), PyObject *obj)
}
return PyLong_FromVoidPtr(bc);
}

static int
count_tlbc_blocks(PyObject *obj, Py_ssize_t *count)
{
if (PyCode_Check(obj)) {
_PyCodeArray *tlbc = ((PyCodeObject *)obj)->co_tlbc;
// First entry always points to the bytecode at the end of the code
// object. Exclude it from the count as it is allocated as part of
// creating the code object.
for (Py_ssize_t i = 1; i < tlbc->size; i++) {
if (tlbc->entries[i] != NULL) {
(*count)++;
}
}
}
return 1;
}

// Return the total number of thread-local bytecode copies, excluding the
// copies that are embedded in the code object.
static PyObject *
get_tlbc_blocks(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(ignored))
{
Py_ssize_t count = 0;
PyUnstable_GC_VisitObjects((gcvisitobjects_t) count_tlbc_blocks, &count);
return PyLong_FromSsize_t(count);
}
#endif

static PyObject *
Expand Down Expand Up @@ -2180,6 +2207,7 @@ static PyMethodDef module_functions[] = {
{"py_thread_id", get_py_thread_id, METH_NOARGS},
{"get_tlbc", get_tlbc, METH_O, NULL},
{"get_tlbc_id", get_tlbc_id, METH_O, NULL},
{"get_tlbc_blocks", get_tlbc_blocks, METH_NOARGS},
#endif
{"suppress_immortalization", suppress_immortalization, METH_O},
{"get_immortalize_deferred", get_immortalize_deferred, METH_NOARGS},
Expand Down

0 comments on commit 7c9da24

Please sign in to comment.