Skip to content

gh-100227: Use an Array for _PyRuntime's Set of Locks During Init #103315

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 48 additions & 60 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -354,47 +354,29 @@ _Py_COMP_DIAG_IGNORE_DEPR_DECLS
static const _PyRuntimeState initial = _PyRuntimeState_INIT(_PyRuntime);
_Py_COMP_DIAG_POP

#define NUMLOCKS 4

static int
alloc_for_runtime(PyThread_type_lock *plock1, PyThread_type_lock *plock2,
PyThread_type_lock *plock3, PyThread_type_lock *plock4)
alloc_for_runtime(PyThread_type_lock locks[NUMLOCKS])
{
/* Force default allocator, since _PyRuntimeState_Fini() must
use the same allocator than this function. */
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);

PyThread_type_lock lock1 = PyThread_allocate_lock();
if (lock1 == NULL) {
return -1;
}

PyThread_type_lock lock2 = PyThread_allocate_lock();
if (lock2 == NULL) {
PyThread_free_lock(lock1);
return -1;
}

PyThread_type_lock lock3 = PyThread_allocate_lock();
if (lock3 == NULL) {
PyThread_free_lock(lock1);
PyThread_free_lock(lock2);
return -1;
}

PyThread_type_lock lock4 = PyThread_allocate_lock();
if (lock4 == NULL) {
PyThread_free_lock(lock1);
PyThread_free_lock(lock2);
PyThread_free_lock(lock3);
return -1;
for (int i = 0; i < NUMLOCKS; i++) {
PyThread_type_lock lock = PyThread_allocate_lock();
if (lock == NULL) {
for (int j = 0; j < i; j++) {
PyThread_free_lock(locks[j]);
locks[j] = NULL;
}
break;
}
locks[i] = lock;
}

PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);

*plock1 = lock1;
*plock2 = lock2;
*plock3 = lock3;
*plock4 = lock4;
return 0;
}

Expand All @@ -403,10 +385,7 @@ init_runtime(_PyRuntimeState *runtime,
void *open_code_hook, void *open_code_userdata,
_Py_AuditHookEntry *audit_hook_head,
Py_ssize_t unicode_next_index,
PyThread_type_lock unicode_ids_mutex,
PyThread_type_lock interpreters_mutex,
PyThread_type_lock xidregistry_mutex,
PyThread_type_lock getargs_mutex)
PyThread_type_lock locks[NUMLOCKS])
{
if (runtime->_initialized) {
Py_FatalError("runtime already initialized");
Expand All @@ -424,17 +403,21 @@ init_runtime(_PyRuntimeState *runtime,

PyPreConfig_InitPythonConfig(&runtime->preconfig);

runtime->interpreters.mutex = interpreters_mutex;

runtime->xidregistry.mutex = xidregistry_mutex;

runtime->getargs.mutex = getargs_mutex;
PyThread_type_lock *lockptrs[NUMLOCKS] = {
&runtime->interpreters.mutex,
&runtime->xidregistry.mutex,
&runtime->getargs.mutex,
&runtime->unicode_state.ids.lock,
};
for (int i = 0; i < NUMLOCKS; i++) {
assert(locks[i] != NULL);
*lockptrs[i] = locks[i];
}

// Set it to the ID of the main thread of the main interpreter.
runtime->main_thread = PyThread_get_thread_ident();

runtime->unicode_state.ids.next_index = unicode_next_index;
runtime->unicode_state.ids.lock = unicode_ids_mutex;

runtime->_initialized = 1;
}
Expand All @@ -452,8 +435,8 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
// is called multiple times.
Py_ssize_t unicode_next_index = runtime->unicode_state.ids.next_index;

PyThread_type_lock lock1, lock2, lock3, lock4;
if (alloc_for_runtime(&lock1, &lock2, &lock3, &lock4) != 0) {
PyThread_type_lock locks[NUMLOCKS];
if (alloc_for_runtime(locks) != 0) {
return _PyStatus_NO_MEMORY();
}

Expand All @@ -474,7 +457,7 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
}

init_runtime(runtime, open_code_hook, open_code_userdata, audit_hook_head,
unicode_next_index, lock1, lock2, lock3, lock4);
unicode_next_index, locks);

return _PyStatus_OK();
}
Expand Down Expand Up @@ -504,10 +487,15 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime)
LOCK = NULL; \
}

FREE_LOCK(runtime->interpreters.mutex);
FREE_LOCK(runtime->xidregistry.mutex);
FREE_LOCK(runtime->unicode_state.ids.lock);
FREE_LOCK(runtime->getargs.mutex);
PyThread_type_lock *lockptrs[NUMLOCKS] = {
&runtime->interpreters.mutex,
&runtime->xidregistry.mutex,
&runtime->getargs.mutex,
&runtime->unicode_state.ids.lock,
};
for (int i = 0; i < NUMLOCKS; i++) {
FREE_LOCK(*lockptrs[i]);
}

#undef FREE_LOCK
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
Expand All @@ -527,25 +515,25 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);

int reinit_interp = _PyThread_at_fork_reinit(&runtime->interpreters.mutex);
int reinit_xidregistry = _PyThread_at_fork_reinit(&runtime->xidregistry.mutex);
int reinit_unicode_ids = _PyThread_at_fork_reinit(&runtime->unicode_state.ids.lock);
int reinit_getargs = _PyThread_at_fork_reinit(&runtime->getargs.mutex);
PyThread_type_lock *lockptrs[NUMLOCKS] = {
&runtime->interpreters.mutex,
&runtime->xidregistry.mutex,
&runtime->getargs.mutex,
&runtime->unicode_state.ids.lock,
};
int reinit_err = 0;
for (int i = 0; i < NUMLOCKS; i++) {
reinit_err += _PyThread_at_fork_reinit(lockptrs[i]);
}

PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);

/* bpo-42540: id_mutex is freed by _PyInterpreterState_Delete, which does
* not force the default allocator. */
int reinit_main_id = _PyThread_at_fork_reinit(&runtime->interpreters.main->id_mutex);
reinit_err += _PyThread_at_fork_reinit(&runtime->interpreters.main->id_mutex);

if (reinit_interp < 0
|| reinit_main_id < 0
|| reinit_xidregistry < 0
|| reinit_unicode_ids < 0
|| reinit_getargs < 0)
{
if (reinit_err < 0) {
return _PyStatus_ERR("Failed to reinitialize runtime locks");

}

PyStatus status = gilstate_tss_reinit(runtime);
Expand Down