Skip to content

Commit 7b3c252

Browse files
authored
bpo-39877: _PyRuntimeState.finalizing becomes atomic (GH-18816)
Convert _PyRuntimeState.finalizing field to an atomic variable: * Rename it to _finalizing * Change its type to _Py_atomic_address * Add _PyRuntimeState_GetFinalizing() and _PyRuntimeState_SetFinalizing() functions * Remove _Py_CURRENTLY_FINALIZING() function: replace it with testing directly _PyRuntimeState_GetFinalizing() value Convert _PyRuntimeState_GetThreadState() to static inline function.
1 parent 5572870 commit 7b3c252

File tree

5 files changed

+29
-15
lines changed

5 files changed

+29
-15
lines changed

Include/internal/pycore_pystate.h

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,11 @@ typedef struct pyruntimestate {
223223
int initialized;
224224

225225
/* Set by Py_FinalizeEx(). Only reset to NULL if Py_Initialize()
226-
is called again. */
227-
PyThreadState *finalizing;
226+
is called again.
227+
228+
Use _PyRuntimeState_GetFinalizing() and _PyRuntimeState_SetFinalizing()
229+
to access it, don't access it directly. */
230+
_Py_atomic_address _finalizing;
228231

229232
struct pyinterpreters {
230233
PyThread_type_lock mutex;
@@ -279,17 +282,25 @@ PyAPI_FUNC(PyStatus) _PyRuntime_Initialize(void);
279282

280283
PyAPI_FUNC(void) _PyRuntime_Finalize(void);
281284

282-
#define _Py_CURRENTLY_FINALIZING(runtime, tstate) \
283-
(runtime->finalizing == tstate)
285+
static inline PyThreadState*
286+
_PyRuntimeState_GetFinalizing(_PyRuntimeState *runtime) {
287+
return (PyThreadState*)_Py_atomic_load_relaxed(&runtime->_finalizing);
288+
}
289+
290+
static inline void
291+
_PyRuntimeState_SetFinalizing(_PyRuntimeState *runtime, PyThreadState *tstate) {
292+
_Py_atomic_store_relaxed(&runtime->_finalizing, (uintptr_t)tstate);
293+
}
284294

285295
PyAPI_FUNC(int) _Py_IsMainInterpreter(PyThreadState* tstate);
286296

287297

288298
/* Variable and macro for in-line access to current thread
289299
and interpreter state */
290300

291-
#define _PyRuntimeState_GetThreadState(runtime) \
292-
((PyThreadState*)_Py_atomic_load_relaxed(&(runtime)->gilstate.tstate_current))
301+
static inline PyThreadState* _PyRuntimeState_GetThreadState(_PyRuntimeState *runtime) {
302+
return (PyThreadState*)_Py_atomic_load_relaxed(&runtime->gilstate.tstate_current);
303+
}
293304

294305
/* Get the current Python thread state.
295306

Python/ceval.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,8 @@ exit_thread_if_finalizing(PyThreadState *tstate)
240240
{
241241
_PyRuntimeState *runtime = tstate->interp->runtime;
242242
/* _Py_Finalizing is protected by the GIL */
243-
if (runtime->finalizing != NULL && !_Py_CURRENTLY_FINALIZING(runtime, tstate)) {
243+
PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
244+
if (finalizing != NULL && finalizing != tstate) {
244245
drop_gil(&runtime->ceval, tstate);
245246
PyThread_exit_thread();
246247
}

Python/pylifecycle.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ _PyRuntime_Finalize(void)
103103
int
104104
_Py_IsFinalizing(void)
105105
{
106-
return _PyRuntime.finalizing != NULL;
106+
return _PyRuntimeState_GetFinalizing(&_PyRuntime) != NULL;
107107
}
108108

109109
/* Hack to force loading of object files */
@@ -507,7 +507,7 @@ pycore_init_runtime(_PyRuntimeState *runtime,
507507
* threads still hanging around from a previous Py_Initialize/Finalize
508508
* pair :(
509509
*/
510-
runtime->finalizing = NULL;
510+
_PyRuntimeState_SetFinalizing(runtime, NULL);
511511

512512
PyStatus status = _Py_HashRandomization_Init(config);
513513
if (_PyStatus_EXCEPTION(status)) {
@@ -1366,7 +1366,7 @@ Py_FinalizeEx(void)
13661366

13671367
/* Remaining threads (e.g. daemon threads) will automatically exit
13681368
after taking the GIL (in PyEval_RestoreThread()). */
1369-
runtime->finalizing = tstate;
1369+
_PyRuntimeState_SetFinalizing(runtime, tstate);
13701370
runtime->initialized = 0;
13711371
runtime->core_initialized = 0;
13721372

@@ -2131,8 +2131,9 @@ static void
21312131
fatal_error_dump_runtime(FILE *stream, _PyRuntimeState *runtime)
21322132
{
21332133
fprintf(stream, "Python runtime state: ");
2134-
if (runtime->finalizing) {
2135-
fprintf(stream, "finalizing (tstate=%p)", runtime->finalizing);
2134+
PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
2135+
if (finalizing) {
2136+
fprintf(stream, "finalizing (tstate=%p)", finalizing);
21362137
}
21372138
else if (runtime->initialized) {
21382139
fprintf(stream, "initialized");

Python/pystate.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ PyInterpreterState_Clear(PyInterpreterState *interp)
292292
Py_CLEAR(interp->after_forkers_parent);
293293
Py_CLEAR(interp->after_forkers_child);
294294
#endif
295-
if (runtime->finalizing == NULL) {
295+
if (_PyRuntimeState_GetFinalizing(runtime) == NULL) {
296296
_PyWarnings_Fini(interp);
297297
}
298298
// XXX Once we have one allocator per interpreter (i.e.

Python/sysmodule.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,9 @@ _PySys_ClearAuditHooks(void)
289289
/* Must be finalizing to clear hooks */
290290
_PyRuntimeState *runtime = &_PyRuntime;
291291
PyThreadState *ts = _PyRuntimeState_GetThreadState(runtime);
292-
assert(!ts || _Py_CURRENTLY_FINALIZING(runtime, ts));
293-
if (!ts || !_Py_CURRENTLY_FINALIZING(runtime, ts)) {
292+
PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
293+
assert(!ts || finalizing == ts);
294+
if (!ts || finalizing != ts) {
294295
return;
295296
}
296297

0 commit comments

Comments
 (0)