Skip to content

Commit 4ced3a1

Browse files
committed
Use the parking lot to avoid eating CPU cycles.
1 parent ac58a16 commit 4ced3a1

2 files changed

Lines changed: 40 additions & 3 deletions

File tree

Python/pylifecycle.c

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "pycore_object.h" // _PyDebug_PrintTotalRefs()
2020
#include "pycore_obmalloc.h" // _PyMem_init_obmalloc()
2121
#include "pycore_optimizer.h" // _Py_Executors_InvalidateAll
22+
#include "pycore_parking_lot.h" // _PyParkingLot
2223
#include "pycore_pathconfig.h" // _PyPathConfig_UpdateGlobal()
2324
#include "pycore_pyerrors.h" // _PyErr_Occurred()
2425
#include "pycore_pylifecycle.h" // _PyErr_Print()
@@ -2314,7 +2315,39 @@ make_pre_finalization_calls(PyThreadState *tstate, int subinterpreters)
23142315
finalize_subinterpreters();
23152316
}
23162317

2318+
// This is used as a throttle to prevent constant spinning while
2319+
// on finalization guards.
2320+
for (;;) {
2321+
uintptr_t num_guards = _Py_atomic_load_uintptr(&interp->finalization_guards);
2322+
if (num_guards == 0) {
2323+
break;
2324+
}
23172325

2326+
int ret = _PyParkingLot_Park(&interp->finalization_guards,
2327+
&num_guards, sizeof(num_guards), -1,
2328+
NULL, /*detach=*/1);
2329+
if (ret == Py_PARK_OK) {
2330+
break;
2331+
}
2332+
else if (ret == Py_PARK_INTR) {
2333+
if (PyErr_CheckSignals() < 0) {
2334+
int fatal = PyErr_ExceptionMatches(PyExc_KeyboardInterrupt);
2335+
PyErr_FormatUnraisable("Exception ignored while waiting on finalization guards");
2336+
if (fatal) {
2337+
fputs("Interrupted while waiting on finalization guards", stderr);
2338+
exit(1);
2339+
}
2340+
}
2341+
assert(!PyErr_Occurred());
2342+
}
2343+
else if (ret == Py_PARK_AGAIN) {
2344+
continue;
2345+
}
2346+
else {
2347+
assert(ret == Py_PARK_TIMEOUT);
2348+
Py_UNREACHABLE();
2349+
}
2350+
}
23182351

23192352
/* Stop the world to prevent other threads from creating threads or
23202353
* atexit callbacks. On the default build, this is simply locked by
@@ -2329,13 +2362,13 @@ make_pre_finalization_calls(PyThreadState *tstate, int subinterpreters)
23292362
int has_subinterpreters = subinterpreters
23302363
? runtime_has_subinterpreters(interp->runtime)
23312364
: 0;
2332-
uintptr_t finalization_guards_expected = 0;
2365+
uintptr_t guards_expected = 0;
23332366
int should_continue = (interp_has_threads(interp)
23342367
|| interp_has_atexit_callbacks(interp)
23352368
|| interp_has_pending_calls(interp)
23362369
|| has_subinterpreters
23372370
|| _Py_atomic_compare_exchange_uintptr(&interp->finalization_guards,
2338-
&finalization_guards_expected,
2371+
&guards_expected,
23392372
_PyInterpreterGuard_GUARDS_NOT_ALLOWED) == 0);
23402373
if (!should_continue) {
23412374
break;

Python/pystate.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3381,8 +3381,12 @@ PyInterpreterGuard_Close(PyInterpreterGuard *guard)
33813381
assert(interp != NULL);
33823382

33833383
assert(_Py_atomic_load_uintptr(&interp->finalization_guards) != _PyInterpreterGuard_GUARDS_NOT_ALLOWED);
3384-
_Py_atomic_add_uintptr(&interp->finalization_guards, -1);
3384+
uintptr_t old_value = _Py_atomic_add_uintptr(&interp->finalization_guards, -1);
3385+
if (old_value == 1) {
3386+
_PyParkingLot_UnparkAll(&interp->finalization_guards);
3387+
}
33853388

3389+
assert(old_value > 0);
33863390
PyMem_RawFree(guard);
33873391
}
33883392

0 commit comments

Comments
 (0)