Skip to content

Commit cf6110b

Browse files
authored
gh-111924: Use PyMutex for Runtime-global Locks. (gh-112207)
This replaces some usages of PyThread_type_lock with PyMutex, which does not require memory allocation to initialize. This simplifies some of the runtime initialization and is also one step towards avoiding changing the default raw memory allocator during initialize/finalization, which can be non-thread-safe in some circumstances.
1 parent db46073 commit cf6110b

18 files changed

+97
-241
lines changed

Include/internal/pycore_atexit.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#ifndef Py_INTERNAL_ATEXIT_H
22
#define Py_INTERNAL_ATEXIT_H
3+
4+
#include "pycore_lock.h" // PyMutex
5+
36
#ifdef __cplusplus
47
extern "C" {
58
#endif
@@ -15,7 +18,7 @@ extern "C" {
1518
typedef void (*atexit_callbackfunc)(void);
1619

1720
struct _atexit_runtime_state {
18-
PyThread_type_lock mutex;
21+
PyMutex mutex;
1922
#define NEXITFUNCS 32
2023
atexit_callbackfunc callbacks[NEXITFUNCS];
2124
int ncallbacks;

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ PyAPI_FUNC(int) _PyEval_MakePendingCalls(PyThreadState *);
4141
#endif
4242

4343
extern void _Py_FinishPendingCalls(PyThreadState *tstate);
44-
extern void _PyEval_InitState(PyInterpreterState *, PyThread_type_lock);
45-
extern void _PyEval_FiniState(struct _ceval_state *ceval);
44+
extern void _PyEval_InitState(PyInterpreterState *);
4645
extern void _PyEval_SignalReceived(PyInterpreterState *interp);
4746

4847
// bitwise flags:

Include/internal/pycore_ceval_state.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11+
#include "pycore_lock.h" // PyMutex
1112
#include "pycore_gil.h" // struct _gil_runtime_state
1213

1314

1415
typedef int (*_Py_pending_call_func)(void *);
1516

1617
struct _pending_calls {
1718
int busy;
18-
PyThread_type_lock lock;
19+
PyMutex mutex;
1920
/* Request for running pending calls. */
2021
int32_t calls_to_do;
2122
#define NPENDINGCALLS 32

Include/internal/pycore_crossinterp.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11+
#include "pycore_lock.h" // PyMutex
1112
#include "pycore_pyerrors.h"
1213

1314

@@ -128,7 +129,7 @@ struct _xidregitem {
128129
struct _xidregistry {
129130
int global; /* builtin types or heap types */
130131
int initialized;
131-
PyThread_type_lock mutex;
132+
PyMutex mutex;
132133
struct _xidregitem *head;
133134
};
134135

Include/internal/pycore_import.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ extern "C" {
99
# error "this header requires Py_BUILD_CORE define"
1010
#endif
1111

12+
#include "pycore_lock.h" // PyMutex
1213
#include "pycore_hashtable.h" // _Py_hashtable_t
1314
#include "pycore_time.h" // _PyTime_t
1415

@@ -47,7 +48,7 @@ struct _import_runtime_state {
4748
Py_ssize_t last_module_index;
4849
struct {
4950
/* A lock to guard the cache. */
50-
PyThread_type_lock mutex;
51+
PyMutex mutex;
5152
/* The actual cache of (filename, name, PyModuleDef) for modules.
5253
Only legacy (single-phase init) extension modules are added
5354
and only if they support multiple initialization (m_size >- 0)

Include/internal/pycore_lock.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ PyMutex_IsLocked(PyMutex *m)
9292
return (_Py_atomic_load_uint8(&m->v) & _Py_LOCKED) != 0;
9393
}
9494

95+
// Re-initializes the mutex after a fork to the unlocked state.
96+
static inline void
97+
_PyMutex_at_fork_reinit(PyMutex *m)
98+
{
99+
memset(m, 0, sizeof(*m));
100+
}
101+
95102
typedef enum _PyLockFlags {
96103
// Do not detach/release the GIL when waiting on the lock.
97104
_Py_LOCK_DONT_DETACH = 0,
@@ -108,6 +115,16 @@ typedef enum _PyLockFlags {
108115
extern PyLockStatus
109116
_PyMutex_LockTimed(PyMutex *m, _PyTime_t timeout_ns, _PyLockFlags flags);
110117

118+
// Lock a mutex with aditional options. See _PyLockFlags for details.
119+
static inline void
120+
PyMutex_LockFlags(PyMutex *m, _PyLockFlags flags)
121+
{
122+
uint8_t expected = _Py_UNLOCKED;
123+
if (!_Py_atomic_compare_exchange_uint8(&m->v, &expected, _Py_LOCKED)) {
124+
_PyMutex_LockTimed(m, -1, flags);
125+
}
126+
}
127+
111128
// Unlock a mutex, returns 0 if the mutex is not locked (used for improved
112129
// error messages).
113130
extern int _PyMutex_TryUnlock(PyMutex *m);

Include/internal/pycore_pymem.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#ifndef Py_INTERNAL_PYMEM_H
22
#define Py_INTERNAL_PYMEM_H
3+
4+
#include "pycore_lock.h" // PyMutex
5+
36
#ifdef __cplusplus
47
extern "C" {
58
#endif
@@ -30,7 +33,7 @@ typedef struct {
3033
} debug_alloc_api_t;
3134

3235
struct _pymem_allocators {
33-
PyThread_type_lock mutex;
36+
PyMutex mutex;
3437
struct {
3538
PyMemAllocatorEx raw;
3639
PyMemAllocatorEx mem;

Include/internal/pycore_pystate.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,9 @@ PyAPI_FUNC(int) _PyState_AddModule(
220220
extern int _PyOS_InterruptOccurred(PyThreadState *tstate);
221221

222222
#define HEAD_LOCK(runtime) \
223-
PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK)
223+
PyMutex_LockFlags(&(runtime)->interpreters.mutex, _Py_LOCK_DONT_DETACH)
224224
#define HEAD_UNLOCK(runtime) \
225-
PyThread_release_lock((runtime)->interpreters.mutex)
225+
PyMutex_Unlock(&(runtime)->interpreters.mutex)
226226

227227
// Get the configuration of the current interpreter.
228228
// The caller must hold the GIL.

Include/internal/pycore_runtime.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ typedef struct pyruntimestate {
173173
unsigned long _finalizing_id;
174174

175175
struct pyinterpreters {
176-
PyThread_type_lock mutex;
176+
PyMutex mutex;
177177
/* The linked list of interpreters, newest first. */
178178
PyInterpreterState *head;
179179
/* The runtime's initial interpreter, which has a special role
@@ -234,7 +234,7 @@ typedef struct pyruntimestate {
234234
Py_OpenCodeHookFunction open_code_hook;
235235
void *open_code_userdata;
236236
struct {
237-
PyThread_type_lock mutex;
237+
PyMutex mutex;
238238
_Py_AuditHookEntry *head;
239239
} audit_hooks;
240240

Include/internal/pycore_unicodeobject.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11+
#include "pycore_lock.h" // PyMutex
1112
#include "pycore_fileutils.h" // _Py_error_handler
1213
#include "pycore_identifier.h" // _Py_Identifier
1314
#include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI
@@ -277,7 +278,7 @@ extern PyTypeObject _PyUnicodeASCIIIter_Type;
277278
/* --- Other API ---------------------------------------------------------- */
278279

279280
struct _Py_unicode_runtime_ids {
280-
PyThread_type_lock lock;
281+
PyMutex mutex;
281282
// next_index value must be preserved when Py_Initialize()/Py_Finalize()
282283
// is called multiple times: see _PyUnicode_FromId() implementation.
283284
Py_ssize_t next_index;

0 commit comments

Comments
 (0)