[Python-checkins] gh-100227: Lock Around Use of the Global "atexit" State (gh-105514)
ericsnowcurrently
webhook-mailer at python.org
Thu Jun 8 14:08:35 EDT 2023
https://github.com/python/cpython/commit/7799c8e678f759c7787785c6287140abe641d1b9
commit: 7799c8e678f759c7787785c6287140abe641d1b9
branch: main
author: Eric Snow <ericsnowcurrently at gmail.com>
committer: ericsnowcurrently <ericsnowcurrently at gmail.com>
date: 2023-06-08T18:08:28Z
summary:
gh-100227: Lock Around Use of the Global "atexit" State (gh-105514)
The risk of a race with this state is relatively low, but we play it safe anyway.
files:
M Include/internal/pycore_atexit.h
M Python/pylifecycle.c
M Python/pystate.c
diff --git a/Include/internal/pycore_atexit.h b/Include/internal/pycore_atexit.h
index b4663b396852..63a2cd5d507d 100644
--- a/Include/internal/pycore_atexit.h
+++ b/Include/internal/pycore_atexit.h
@@ -15,6 +15,7 @@ extern "C" {
typedef void (*atexit_callbackfunc)(void);
struct _atexit_runtime_state {
+ PyThread_type_lock mutex;
#define NEXITFUNCS 32
atexit_callbackfunc callbacks[NEXITFUNCS];
int ncallbacks;
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 4c21160d3124..9ac5630959f8 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -2973,24 +2973,35 @@ wait_for_thread_shutdown(PyThreadState *tstate)
int Py_AtExit(void (*func)(void))
{
- if (_PyRuntime.atexit.ncallbacks >= NEXITFUNCS)
+ struct _atexit_runtime_state *state = &_PyRuntime.atexit;
+ PyThread_acquire_lock(state->mutex, WAIT_LOCK);
+ if (state->ncallbacks >= NEXITFUNCS) {
+ PyThread_release_lock(state->mutex);
return -1;
- _PyRuntime.atexit.callbacks[_PyRuntime.atexit.ncallbacks++] = func;
+ }
+ state->callbacks[state->ncallbacks++] = func;
+ PyThread_release_lock(state->mutex);
return 0;
}
static void
call_ll_exitfuncs(_PyRuntimeState *runtime)
{
+ atexit_callbackfunc exitfunc;
struct _atexit_runtime_state *state = &runtime->atexit;
+
+ PyThread_acquire_lock(state->mutex, WAIT_LOCK);
while (state->ncallbacks > 0) {
/* pop last function from the list */
state->ncallbacks--;
- atexit_callbackfunc exitfunc = state->callbacks[state->ncallbacks];
+ exitfunc = state->callbacks[state->ncallbacks];
state->callbacks[state->ncallbacks] = NULL;
+ PyThread_release_lock(state->mutex);
exitfunc();
+ PyThread_acquire_lock(state->mutex, WAIT_LOCK);
}
+ PyThread_release_lock(state->mutex);
fflush(stdout);
fflush(stderr);
diff --git a/Python/pystate.c b/Python/pystate.c
index 5b7a6c86ade4..a1f14f76e751 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -380,7 +380,16 @@ _Py_COMP_DIAG_IGNORE_DEPR_DECLS
static const _PyRuntimeState initial = _PyRuntimeState_INIT(_PyRuntime);
_Py_COMP_DIAG_POP
-#define NUMLOCKS 5
+#define NUMLOCKS 6
+#define LOCKS_INIT(runtime) \
+ { \
+ &(runtime)->interpreters.mutex, \
+ &(runtime)->xidregistry.mutex, \
+ &(runtime)->getargs.mutex, \
+ &(runtime)->unicode_state.ids.lock, \
+ &(runtime)->imports.extensions.mutex, \
+ &(runtime)->atexit.mutex, \
+ }
static int
alloc_for_runtime(PyThread_type_lock locks[NUMLOCKS])
@@ -427,13 +436,7 @@ init_runtime(_PyRuntimeState *runtime,
PyPreConfig_InitPythonConfig(&runtime->preconfig);
- PyThread_type_lock *lockptrs[NUMLOCKS] = {
- &runtime->interpreters.mutex,
- &runtime->xidregistry.mutex,
- &runtime->getargs.mutex,
- &runtime->unicode_state.ids.lock,
- &runtime->imports.extensions.mutex,
- };
+ PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime);
for (int i = 0; i < NUMLOCKS; i++) {
assert(locks[i] != NULL);
*lockptrs[i] = locks[i];
@@ -512,13 +515,7 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime)
LOCK = NULL; \
}
- PyThread_type_lock *lockptrs[NUMLOCKS] = {
- &runtime->interpreters.mutex,
- &runtime->xidregistry.mutex,
- &runtime->getargs.mutex,
- &runtime->unicode_state.ids.lock,
- &runtime->imports.extensions.mutex,
- };
+ PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime);
for (int i = 0; i < NUMLOCKS; i++) {
FREE_LOCK(*lockptrs[i]);
}
@@ -541,13 +538,7 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
- PyThread_type_lock *lockptrs[NUMLOCKS] = {
- &runtime->interpreters.mutex,
- &runtime->xidregistry.mutex,
- &runtime->getargs.mutex,
- &runtime->unicode_state.ids.lock,
- &runtime->imports.extensions.mutex,
- };
+ PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime);
int reinit_err = 0;
for (int i = 0; i < NUMLOCKS; i++) {
reinit_err += _PyThread_at_fork_reinit(lockptrs[i]);
More information about the Python-checkins
mailing list