gh-111924: Use PyMutex for runtime global locks. by colesbury · Pull Request #112207 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
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
5 changes: 4 additions & 1 deletion Include/internal/pycore_atexit.h
3 changes: 1 addition & 2 deletions Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ PyAPI_FUNC(int) _PyEval_MakePendingCalls(PyThreadState *);
#endif

extern void _Py_FinishPendingCalls(PyThreadState *tstate);
extern void _PyEval_InitState(PyInterpreterState *, PyThread_type_lock);
extern void _PyEval_FiniState(struct _ceval_state *ceval);
extern void _PyEval_InitState(PyInterpreterState *);
extern void _PyEval_SignalReceived(PyInterpreterState *interp);

// bitwise flags:
Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_ceval_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif

#include "pycore_lock.h" // PyMutex
#include "pycore_gil.h" // struct _gil_runtime_state


typedef int (*_Py_pending_call_func)(void *);

struct _pending_calls {
int busy;
PyThread_type_lock lock;
PyMutex mutex;
/* Request for running pending calls. */
int32_t calls_to_do;
#define NPENDINGCALLS 32
Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_crossinterp.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif

#include "pycore_lock.h" // PyMutex
#include "pycore_pyerrors.h"


Expand Down Expand Up @@ -128,7 +129,7 @@ struct _xidregitem {
struct _xidregistry {
int global; /* builtin types or heap types */
int initialized;
PyThread_type_lock mutex;
PyMutex mutex;
struct _xidregitem *head;
};

Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_import.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif

#include "pycore_lock.h" // PyMutex
#include "pycore_hashtable.h" // _Py_hashtable_t
#include "pycore_time.h" // _PyTime_t

Expand Down Expand Up @@ -47,7 +48,7 @@ struct _import_runtime_state {
Py_ssize_t last_module_index;
struct {
/* A lock to guard the cache. */
PyThread_type_lock mutex;
PyMutex mutex;
/* The actual cache of (filename, name, PyModuleDef) for modules.
Only legacy (single-phase init) extension modules are added
and only if they support multiple initialization (m_size >- 0)
Expand Down
17 changes: 17 additions & 0 deletions Include/internal/pycore_lock.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ PyMutex_IsLocked(PyMutex *m)
return (_Py_atomic_load_uint8(&m->v) & _Py_LOCKED) != 0;
}

// Re-initializes the mutex after a fork to the unlocked state.
static inline void
_PyMutex_at_fork_reinit(PyMutex *m)
{
memset(m, 0, sizeof(*m));
Comment thread
ericsnowcurrently marked this conversation as resolved.
}

typedef enum _PyLockFlags {
// Do not detach/release the GIL when waiting on the lock.
_Py_LOCK_DONT_DETACH = 0,
Expand All @@ -108,6 +115,16 @@ typedef enum _PyLockFlags {
extern PyLockStatus
_PyMutex_LockTimed(PyMutex *m, _PyTime_t timeout_ns, _PyLockFlags flags);

// Lock a mutex with aditional options. See _PyLockFlags for details.
static inline void
PyMutex_LockFlags(PyMutex *m, _PyLockFlags flags)
{
uint8_t expected = _Py_UNLOCKED;
if (!_Py_atomic_compare_exchange_uint8(&m->v, &expected, _Py_LOCKED)) {
_PyMutex_LockTimed(m, -1, flags);
}
}

// Unlock a mutex, returns 0 if the mutex is not locked (used for improved
// error messages).
extern int _PyMutex_TryUnlock(PyMutex *m);
Expand Down
5 changes: 4 additions & 1 deletion Include/internal/pycore_pymem.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#ifndef Py_INTERNAL_PYMEM_H
#define Py_INTERNAL_PYMEM_H

#include "pycore_lock.h" // PyMutex

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -30,7 +33,7 @@ typedef struct {
} debug_alloc_api_t;

struct _pymem_allocators {
PyThread_type_lock mutex;
PyMutex mutex;
struct {
PyMemAllocatorEx raw;
PyMemAllocatorEx mem;
Expand Down
4 changes: 2 additions & 2 deletions Include/internal/pycore_pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,9 @@ PyAPI_FUNC(int) _PyState_AddModule(
extern int _PyOS_InterruptOccurred(PyThreadState *tstate);

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

// Get the configuration of the current interpreter.
// The caller must hold the GIL.
Expand Down
4 changes: 2 additions & 2 deletions Include/internal/pycore_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ typedef struct pyruntimestate {
unsigned long _finalizing_id;

struct pyinterpreters {
PyThread_type_lock mutex;
PyMutex mutex;
/* The linked list of interpreters, newest first. */
PyInterpreterState *head;
/* The runtime's initial interpreter, which has a special role
Expand Down Expand Up @@ -236,7 +236,7 @@ typedef struct pyruntimestate {
Py_OpenCodeHookFunction open_code_hook;
void *open_code_userdata;
struct {
PyThread_type_lock mutex;
PyMutex mutex;
_Py_AuditHookEntry *head;
} audit_hooks;

Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif

#include "pycore_lock.h" // PyMutex
#include "pycore_fileutils.h" // _Py_error_handler
#include "pycore_identifier.h" // _Py_Identifier
#include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI
Expand Down Expand Up @@ -277,7 +278,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
61 changes: 16 additions & 45 deletions Objects/obmalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,13 +329,9 @@ int
_PyMem_SetDefaultAllocator(PyMemAllocatorDomain domain,
PyMemAllocatorEx *old_alloc)
{
if (ALLOCATORS_MUTEX == NULL) {
/* The runtime must be initializing. */
return set_default_allocator_unlocked(domain, pydebug, old_alloc);
}
PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
PyMutex_Lock(&ALLOCATORS_MUTEX);
int res = set_default_allocator_unlocked(domain, pydebug, old_alloc);
PyThread_release_lock(ALLOCATORS_MUTEX);
PyMutex_Unlock(&ALLOCATORS_MUTEX);
return res;
}

Expand Down Expand Up @@ -467,9 +463,9 @@ set_up_allocators_unlocked(PyMemAllocatorName allocator)
int
_PyMem_SetupAllocators(PyMemAllocatorName allocator)
{
PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
PyMutex_Lock(&ALLOCATORS_MUTEX);
int res = set_up_allocators_unlocked(allocator);
PyThread_release_lock(ALLOCATORS_MUTEX);
PyMutex_Unlock(&ALLOCATORS_MUTEX);
return res;
}

Expand Down Expand Up @@ -554,9 +550,9 @@ get_current_allocator_name_unlocked(void)
const char*
_PyMem_GetCurrentAllocatorName(void)
{
PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
PyMutex_Lock(&ALLOCATORS_MUTEX);
const char *name = get_current_allocator_name_unlocked();
PyThread_release_lock(ALLOCATORS_MUTEX);
PyMutex_Unlock(&ALLOCATORS_MUTEX);
return name;
}

Expand Down Expand Up @@ -653,14 +649,9 @@ set_up_debug_hooks_unlocked(void)
void
PyMem_SetupDebugHooks(void)
{
if (ALLOCATORS_MUTEX == NULL) {
/* The runtime must not be completely initialized yet. */
set_up_debug_hooks_unlocked();
return;
}
PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
PyMutex_Lock(&ALLOCATORS_MUTEX);
set_up_debug_hooks_unlocked();
PyThread_release_lock(ALLOCATORS_MUTEX);
PyMutex_Unlock(&ALLOCATORS_MUTEX);
}

static void
Expand Down Expand Up @@ -696,53 +687,33 @@ set_allocator_unlocked(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)
void
PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)
{
if (ALLOCATORS_MUTEX == NULL) {
/* The runtime must not be completely initialized yet. */
get_allocator_unlocked(domain, allocator);
return;
}
PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
PyMutex_Lock(&ALLOCATORS_MUTEX);
get_allocator_unlocked(domain, allocator);
PyThread_release_lock(ALLOCATORS_MUTEX);
PyMutex_Unlock(&ALLOCATORS_MUTEX);
}

void
PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)
{
if (ALLOCATORS_MUTEX == NULL) {
/* The runtime must not be completely initialized yet. */
set_allocator_unlocked(domain, allocator);
return;
}
PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
PyMutex_Lock(&ALLOCATORS_MUTEX);
set_allocator_unlocked(domain, allocator);
PyThread_release_lock(ALLOCATORS_MUTEX);
PyMutex_Unlock(&ALLOCATORS_MUTEX);
}

void
PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)
{
if (ALLOCATORS_MUTEX == NULL) {
/* The runtime must not be completely initialized yet. */
*allocator = _PyObject_Arena;
return;
}
PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
PyMutex_Lock(&ALLOCATORS_MUTEX);
*allocator = _PyObject_Arena;
PyThread_release_lock(ALLOCATORS_MUTEX);
PyMutex_Unlock(&ALLOCATORS_MUTEX);
}

void
PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)
{
if (ALLOCATORS_MUTEX == NULL) {
/* The runtime must not be completely initialized yet. */
_PyObject_Arena = *allocator;
return;
}
PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
PyMutex_Lock(&ALLOCATORS_MUTEX);
_PyObject_Arena = *allocator;
PyThread_release_lock(ALLOCATORS_MUTEX);
PyMutex_Unlock(&ALLOCATORS_MUTEX);
}


Expand Down
4 changes: 2 additions & 2 deletions Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1904,7 +1904,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_load_ssize(&id->index);
Expand All @@ -1914,7 +1914,7 @@ _PyUnicode_FromId(_Py_Identifier *id)
rt_ids->next_index++;
_Py_atomic_store_ssize(&id->index, index);
}
PyThread_release_lock(rt_ids->lock);
PyMutex_Unlock(&rt_ids->mutex);
}
assert(index >= 0);

Expand Down
37 changes: 9 additions & 28 deletions Python/ceval_gil.c
Loading