diff options
author | Sam Gross <colesbury@gmail.com> | 2024-04-29 18:36:02 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-29 18:36:02 (GMT) |
commit | 7ccacb220d99662b626c8bc63b00a27eaf604f0c (patch) | |
tree | 231cb901ee62e523be237392b0f2b966aa8be128 /Python | |
parent | 8d4b756fd31d4d91b55105b1241561e92cc571a3 (diff) | |
download | cpython-7ccacb220d99662b626c8bc63b00a27eaf604f0c.zip cpython-7ccacb220d99662b626c8bc63b00a27eaf604f0c.tar.gz cpython-7ccacb220d99662b626c8bc63b00a27eaf604f0c.tar.bz2 |
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.
Diffstat (limited to 'Python')
-rw-r--r-- | Python/gc_free_threading.c | 30 | ||||
-rw-r--r-- | Python/pystate.c | 11 |
2 files changed, 41 insertions, 0 deletions
diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 9cf0e98..8c0940d 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -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) { diff --git a/Python/pystate.c b/Python/pystate.c index bca28ce..78b39c9 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -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. |