gh-117783: Immortalize objects that use deferred reference counting (#118112)

Deferred reference counting is not fully implemented yet. As a temporary
measure, we immortalize objects that would use deferred reference
counting to avoid multi-threaded scaling bottlenecks.

This is only performed in the free-threaded build once the first
non-main thread is started. Additionally, some tests, including refleak
tests, suppress this behavior.
This commit is contained in:
Sam Gross 2024-04-29 14:36:02 -04:00 committed by GitHub
parent 8d4b756fd3
commit 7ccacb220d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 134 additions and 8 deletions

View file

@ -704,6 +704,12 @@ _PyGC_Init(PyInterpreterState *interp)
{
GCState *gcstate = &interp->gc;
if (_Py_IsMainInterpreter(interp)) {
// gh-117783: immortalize objects that would use deferred refcounting
// once the first non-main thread is created.
gcstate->immortalize.enable_on_thread_created = 1;
}
gcstate->garbage = PyList_New(0);
if (gcstate->garbage == NULL) {
return _PyStatus_NO_MEMORY();
@ -1781,6 +1787,30 @@ custom_visitor_wrapper(const mi_heap_t *heap, const mi_heap_area_t *area,
return true;
}
// gh-117783: Immortalize objects that use deferred reference counting to
// temporarily work around scaling bottlenecks.
static bool
immortalize_visitor(const mi_heap_t *heap, const mi_heap_area_t *area,
void *block, size_t block_size, void *args)
{
PyObject *op = op_from_block(block, args, false);
if (op != NULL && _PyObject_HasDeferredRefcount(op)) {
_Py_SetImmortal(op);
op->ob_gc_bits &= ~_PyGC_BITS_DEFERRED;
}
return true;
}
void
_PyGC_ImmortalizeDeferredObjects(PyInterpreterState *interp)
{
struct visitor_args args;
_PyEval_StopTheWorld(interp);
gc_visit_heaps(interp, &immortalize_visitor, &args);
interp->gc.immortalize.enabled = 1;
_PyEval_StartTheWorld(interp);
}
void
PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg)
{

View file

@ -1568,6 +1568,17 @@ new_threadstate(PyInterpreterState *interp, int whence)
// Must be called with lock unlocked to avoid re-entrancy deadlock.
PyMem_RawFree(new_tstate);
}
else {
#ifdef Py_GIL_DISABLED
if (interp->gc.immortalize.enable_on_thread_created &&
!interp->gc.immortalize.enabled)
{
// Immortalize objects marked as using deferred reference counting
// the first time a non-main thread is created.
_PyGC_ImmortalizeDeferredObjects(interp);
}
#endif
}
#ifdef Py_GIL_DISABLED
// Must be called with lock unlocked to avoid lock ordering deadlocks.