Skip to content

Commit

Permalink
pystate: use _PyRawMutex for internal mutexes
Browse files Browse the repository at this point in the history
This uses _PyRawMutex instead of PyThread_type_lock for a number of
internal locks in PyRuntime.
  • Loading branch information
colesbury committed Apr 23, 2023
1 parent 7a7aca0 commit 8ed62ca
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 134 deletions.
9 changes: 6 additions & 3 deletions Include/internal/pycore_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ extern "C" {
#include "pycore_unicodeobject.h" // struct _Py_unicode_runtime_ids

struct _getargs_runtime_state {
PyThread_type_lock mutex;
struct _PyArg_Parser *static_parsers;
};

Expand Down Expand Up @@ -96,7 +95,7 @@ typedef struct pyruntimestate {
struct _signals_runtime_state signals;

struct pyinterpreters {
PyThread_type_lock mutex;
_PyRawMutex mutex;
/* The linked list of interpreters, newest first. */
PyInterpreterState *head;
/* The runtime's initial interpreter, which has a special role
Expand All @@ -115,7 +114,7 @@ typedef struct pyruntimestate {
} interpreters;
// XXX Remove this field once we have a tp_* slot.
struct _xidregistry {
PyThread_type_lock mutex;
_PyRawMutex mutex;
struct _xidregitem *head;
} xidregistry;

Expand Down Expand Up @@ -180,6 +179,10 @@ typedef struct pyruntimestate {
PyInterpreterState _main_interpreter;
} _PyRuntimeState;

#define HEAD_LOCK(runtime) \
_PyRawMutex_lock(&(runtime)->interpreters.mutex)
#define HEAD_UNLOCK(runtime) \
_PyRawMutex_unlock(&(runtime)->interpreters.mutex)

/* other API */

Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extern PyTypeObject _PyUnicodeASCIIIter_Type;
/* other API */

struct _Py_unicode_runtime_ids {
PyThread_type_lock lock;
_PyMutex mutex;
// next_index value must be preserved when Py_Initialize()/Py_Finalize()
// is called multiple times: see _PyUnicode_FromId() implementation.
Py_ssize_t next_index;
Expand Down
4 changes: 2 additions & 2 deletions Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1858,7 +1858,7 @@ _PyUnicode_FromId(_Py_Identifier *id)
if (index < 0) {
struct _Py_unicode_runtime_ids *rt_ids = &interp->runtime->unicode_state.ids;

PyThread_acquire_lock(rt_ids->lock, WAIT_LOCK);
_PyMutex_lock(&rt_ids->mutex);
// Check again to detect concurrent access. Another thread can have
// initialized the index while this thread waited for the lock.
index = _Py_atomic_size_get(&id->index);
Expand All @@ -1868,7 +1868,7 @@ _PyUnicode_FromId(_Py_Identifier *id)
rt_ids->next_index++;
_Py_atomic_size_set(&id->index, index);
}
PyThread_release_lock(rt_ids->lock);
_PyMutex_unlock(&rt_ids->mutex);
}
assert(index >= 0);

Expand Down
5 changes: 0 additions & 5 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,6 @@
#define _Py_atomic_load_relaxed_int32(ATOMIC_VAL) _Py_atomic_load_relaxed(ATOMIC_VAL)
#endif

#define HEAD_LOCK(runtime) \
PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK)
#define HEAD_UNLOCK(runtime) \
PyThread_release_lock((runtime)->interpreters.mutex)

/* Forward declarations */
static PyObject *trace_call_function(
PyThreadState *tstate, PyObject *callable, PyObject **stack,
Expand Down
134 changes: 11 additions & 123 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,59 +63,11 @@ _Py_COMP_DIAG_POP
Py_DECL_THREAD PyThreadState *_Py_current_tstate;


static int
alloc_for_runtime(PyThread_type_lock *plock1, PyThread_type_lock *plock2,
PyThread_type_lock *plock3, PyThread_type_lock *plock4)
{
/* 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;
}

PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);

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

static void
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)
Py_ssize_t unicode_next_index)
{
if (runtime->_initialized) {
Py_FatalError("runtime already initialized");
Expand All @@ -133,17 +85,10 @@ init_runtime(_PyRuntimeState *runtime,

PyPreConfig_InitPythonConfig(&runtime->preconfig);

runtime->interpreters.mutex = interpreters_mutex;

runtime->xidregistry.mutex = xidregistry_mutex;

runtime->getargs.mutex = getargs_mutex;

// 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 @@ -161,41 +106,20 @@ _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) {
return _PyStatus_NO_MEMORY();
}

if (runtime->_initialized) {
// Py_Initialize() must be running again.
// Reset to _PyRuntimeState_INIT.
memcpy(runtime, &initial, sizeof(*runtime));
}
init_runtime(runtime, open_code_hook, open_code_userdata, audit_hook_head,
unicode_next_index, lock1, lock2, lock3, lock4);
unicode_next_index);

return _PyStatus_OK();
}

void
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
{
/* Force the allocator used by _PyRuntimeState_Init(). */
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
#define FREE_LOCK(LOCK) \
if (LOCK != NULL) { \
PyThread_free_lock(LOCK); \
LOCK = NULL; \
}

FREE_LOCK(runtime->interpreters.mutex);
FREE_LOCK(runtime->xidregistry.mutex);
FREE_LOCK(runtime->unicode_state.ids.lock);
FREE_LOCK(runtime->getargs.mutex);

#undef FREE_LOCK
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
}

#ifdef HAVE_FORK
Expand All @@ -207,27 +131,14 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
// This was initially set in _PyRuntimeState_Init().
runtime->main_thread = PyThread_get_thread_ident();

/* Force default allocator, since _PyRuntimeState_Fini() must
use the same allocator than this function. */
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);

PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
memset(&runtime->interpreters.mutex, 0, sizeof(runtime->interpreters.mutex));
memset(&runtime->xidregistry.mutex, 0, sizeof(runtime->xidregistry.mutex));

/* 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);

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

Expand All @@ -236,11 +147,6 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
}
#endif

#define HEAD_LOCK(runtime) \
PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK)
#define HEAD_UNLOCK(runtime) \
PyThread_release_lock((runtime)->interpreters.mutex)

/* Forward declaration */
static void _PyGILState_NoteThreadState(
struct _gilstate_runtime_state *gilstate, PyThreadState* tstate);
Expand Down Expand Up @@ -284,24 +190,6 @@ _PyInterpreterState_Enable(_PyRuntimeState *runtime)
{
struct pyinterpreters *interpreters = &runtime->interpreters;
interpreters->next_id = 0;

/* Py_Finalize() calls _PyRuntimeState_Fini() which clears the mutex.
Create a new mutex if needed. */
if (interpreters->mutex == NULL) {
/* Force default allocator, since _PyRuntimeState_Fini() must
use the same allocator than this function. */
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);

interpreters->mutex = PyThread_allocate_lock();

PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);

if (interpreters->mutex == NULL) {
return _PyStatus_ERR("Can't initialize threads for interpreter");
}
}

return _PyStatus_OK();
}

Expand Down Expand Up @@ -2168,12 +2056,12 @@ _PyCrossInterpreterData_RegisterClass(PyTypeObject *cls,
}

struct _xidregistry *xidregistry = &_PyRuntime.xidregistry ;
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
_PyRawMutex_lock(&xidregistry->mutex);
if (xidregistry->head == NULL) {
_register_builtins_for_crossinterpreter_data(xidregistry);
}
int res = _xidregistry_add_type(xidregistry, cls, getdata);
PyThread_release_lock(xidregistry->mutex);
_PyRawMutex_unlock(&xidregistry->mutex);
return res;
}

Expand All @@ -2182,13 +2070,13 @@ _PyCrossInterpreterData_UnregisterClass(PyTypeObject *cls)
{
int res = 0;
struct _xidregistry *xidregistry = &_PyRuntime.xidregistry ;
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
_PyRawMutex_lock(&xidregistry->mutex);
struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
if (matched != NULL) {
(void)_xidregistry_remove_entry(xidregistry, matched);
res = 1;
}
PyThread_release_lock(xidregistry->mutex);
_PyRawMutex_unlock(&xidregistry->mutex);
return res;
}

Expand All @@ -2202,14 +2090,14 @@ _PyCrossInterpreterData_Lookup(PyObject *obj)
{
struct _xidregistry *xidregistry = &_PyRuntime.xidregistry ;
PyObject *cls = PyObject_Type(obj);
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
_PyRawMutex_lock(&xidregistry->mutex);
if (xidregistry->head == NULL) {
_register_builtins_for_crossinterpreter_data(xidregistry);
}
struct _xidregitem *matched = _xidregistry_find_type(xidregistry,
(PyTypeObject *)cls);
Py_DECREF(cls);
PyThread_release_lock(xidregistry->mutex);
_PyRawMutex_unlock(&xidregistry->mutex);
return matched != NULL ? matched->getdata : NULL;
}

Expand Down

0 comments on commit 8ed62ca

Please sign in to comment.