Skip to content

Commit 082f370

Browse files
gh-137514: Add a free-threading wrapper for mutexes (GH-137515)
Add `FT_MUTEX_LOCK`/`FT_MUTEX_UNLOCK`, which call `PyMutex_Lock` and `PyMutex_Unlock` on the free-threaded build, and no-op otherwise.
1 parent dec624e commit 082f370

File tree

9 files changed

+44
-88
lines changed

9 files changed

+44
-88
lines changed

Include/internal/pycore_pyatomic_ft_wrappers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ extern "C" {
111111
_Py_atomic_load_ullong_relaxed(&value)
112112
#define FT_ATOMIC_ADD_SSIZE(value, new_value) \
113113
(void)_Py_atomic_add_ssize(&value, new_value)
114+
#define FT_MUTEX_LOCK(lock) PyMutex_Lock(lock)
115+
#define FT_MUTEX_UNLOCK(lock) PyMutex_Unlock(lock)
114116

115117
#else
116118
#define FT_ATOMIC_LOAD_PTR(value) value
@@ -159,6 +161,8 @@ extern "C" {
159161
#define FT_ATOMIC_LOAD_ULLONG_RELAXED(value) value
160162
#define FT_ATOMIC_STORE_ULLONG_RELAXED(value, new_value) value = new_value
161163
#define FT_ATOMIC_ADD_SSIZE(value, new_value) (void)(value += new_value)
164+
#define FT_MUTEX_LOCK(lock) do {} while (0)
165+
#define FT_MUTEX_UNLOCK(lock) do {} while (0)
162166

163167
#endif
164168

Modules/_ctypes/malloc_closure.c

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,6 @@
3030

3131
#ifdef Py_GIL_DISABLED
3232
static PyMutex malloc_closure_lock;
33-
# define MALLOC_CLOSURE_LOCK() PyMutex_Lock(&malloc_closure_lock)
34-
# define MALLOC_CLOSURE_UNLOCK() PyMutex_Unlock(&malloc_closure_lock)
35-
#else
36-
# define MALLOC_CLOSURE_LOCK() ((void)0)
37-
# define MALLOC_CLOSURE_UNLOCK() ((void)0)
3833
#endif
3934

4035
typedef union _tagITEM {
@@ -120,11 +115,11 @@ void Py_ffi_closure_free(void *p)
120115
}
121116
#endif
122117
#endif
123-
MALLOC_CLOSURE_LOCK();
118+
FT_MUTEX_LOCK(&malloc_closure_lock);
124119
ITEM *item = (ITEM *)p;
125120
item->next = free_list;
126121
free_list = item;
127-
MALLOC_CLOSURE_UNLOCK();
122+
FT_MUTEX_UNLOCK(&malloc_closure_lock);
128123
}
129124

130125
/* return one item from the free list, allocating more if needed */
@@ -143,13 +138,13 @@ void *Py_ffi_closure_alloc(size_t size, void** codeloc)
143138
}
144139
#endif
145140
#endif
146-
MALLOC_CLOSURE_LOCK();
141+
FT_MUTEX_LOCK(&malloc_closure_lock);
147142
ITEM *item;
148143
if (!free_list) {
149144
more_core();
150145
}
151146
if (!free_list) {
152-
MALLOC_CLOSURE_UNLOCK();
147+
FT_MUTEX_UNLOCK(&malloc_closure_lock);
153148
return NULL;
154149
}
155150
item = free_list;
@@ -160,6 +155,6 @@ void *Py_ffi_closure_alloc(size_t size, void** codeloc)
160155
#else
161156
*codeloc = (void *)item;
162157
#endif
163-
MALLOC_CLOSURE_UNLOCK();
158+
FT_MUTEX_UNLOCK(&malloc_closure_lock);
164159
return (void *)item;
165160
}

Objects/codeobject.c

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -550,16 +550,12 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
550550
co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE;
551551
co->co_ncellvars = ncellvars;
552552
co->co_nfreevars = nfreevars;
553-
#ifdef Py_GIL_DISABLED
554-
PyMutex_Lock(&interp->func_state.mutex);
555-
#endif
553+
FT_MUTEX_LOCK(&interp->func_state.mutex);
556554
co->co_version = interp->func_state.next_version;
557555
if (interp->func_state.next_version != 0) {
558556
interp->func_state.next_version++;
559557
}
560-
#ifdef Py_GIL_DISABLED
561-
PyMutex_Unlock(&interp->func_state.mutex);
562-
#endif
558+
FT_MUTEX_UNLOCK(&interp->func_state.mutex);
563559
co->_co_monitoring = NULL;
564560
co->_co_instrumentation_version = 0;
565561
/* not set */
@@ -689,7 +685,7 @@ intern_code_constants(struct _PyCodeConstructor *con)
689685
#ifdef Py_GIL_DISABLED
690686
PyInterpreterState *interp = _PyInterpreterState_GET();
691687
struct _py_code_state *state = &interp->code_state;
692-
PyMutex_Lock(&state->mutex);
688+
FT_MUTEX_LOCK(&state->mutex);
693689
#endif
694690
if (intern_strings(con->names) < 0) {
695691
goto error;
@@ -700,15 +696,11 @@ intern_code_constants(struct _PyCodeConstructor *con)
700696
if (intern_strings(con->localsplusnames) < 0) {
701697
goto error;
702698
}
703-
#ifdef Py_GIL_DISABLED
704-
PyMutex_Unlock(&state->mutex);
705-
#endif
699+
FT_MUTEX_UNLOCK(&state->mutex);
706700
return 0;
707701

708702
error:
709-
#ifdef Py_GIL_DISABLED
710-
PyMutex_Unlock(&state->mutex);
711-
#endif
703+
FT_MUTEX_UNLOCK(&state->mutex);
712704
return -1;
713705
}
714706

Objects/unicodeobject.c

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,6 @@ NOTE: In the interpreter's initialization phase, some globals are currently
114114
# define _PyUnicode_CHECK(op) PyUnicode_Check(op)
115115
#endif
116116

117-
#ifdef Py_GIL_DISABLED
118-
# define LOCK_INTERNED(interp) PyMutex_Lock(&_Py_INTERP_CACHED_OBJECT(interp, interned_mutex))
119-
# define UNLOCK_INTERNED(interp) PyMutex_Unlock(&_Py_INTERP_CACHED_OBJECT(interp, interned_mutex))
120-
#else
121-
# define LOCK_INTERNED(interp)
122-
# define UNLOCK_INTERNED(interp)
123-
#endif
124-
125117
static inline char* _PyUnicode_UTF8(PyObject *op)
126118
{
127119
return FT_ATOMIC_LOAD_PTR_ACQUIRE(_PyCompactUnicodeObject_CAST(op)->utf8);
@@ -15988,14 +15980,16 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */,
1598815980
/* Do a setdefault on the per-interpreter cache. */
1598915981
PyObject *interned = get_interned_dict(interp);
1599015982
assert(interned != NULL);
15991-
15992-
LOCK_INTERNED(interp);
15983+
#ifdef Py_GIL_DISABLED
15984+
# define INTERN_MUTEX &_Py_INTERP_CACHED_OBJECT(interp, interned_mutex)
15985+
#endif
15986+
FT_MUTEX_LOCK(INTERN_MUTEX);
1599315987
PyObject *t;
1599415988
{
1599515989
int res = PyDict_SetDefaultRef(interned, s, s, &t);
1599615990
if (res < 0) {
1599715991
PyErr_Clear();
15998-
UNLOCK_INTERNED(interp);
15992+
FT_MUTEX_UNLOCK(INTERN_MUTEX);
1599915993
return s;
1600015994
}
1600115995
else if (res == 1) {
@@ -16005,7 +15999,7 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */,
1600515999
PyUnicode_CHECK_INTERNED(t) == SSTATE_INTERNED_MORTAL) {
1600616000
immortalize_interned(t);
1600716001
}
16008-
UNLOCK_INTERNED(interp);
16002+
FT_MUTEX_UNLOCK(INTERN_MUTEX);
1600916003
return t;
1601016004
}
1601116005
else {
@@ -16038,7 +16032,7 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */,
1603816032
immortalize_interned(s);
1603916033
}
1604016034

16041-
UNLOCK_INTERNED(interp);
16035+
FT_MUTEX_UNLOCK(INTERN_MUTEX);
1604216036
return s;
1604316037
}
1604416038

Parser/pegen.c

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "pycore_pyerrors.h" // PyExc_IncompleteInputError
66
#include "pycore_runtime.h" // _PyRuntime
77
#include "pycore_unicodeobject.h" // _PyUnicode_InternImmortal
8+
#include "pycore_pyatomic_ft_wrappers.h"
89
#include <errcode.h>
910

1011
#include "lexer/lexer.h"
@@ -299,22 +300,14 @@ _PyPegen_fill_token(Parser *p)
299300
#define NSTATISTICS _PYPEGEN_NSTATISTICS
300301
#define memo_statistics _PyRuntime.parser.memo_statistics
301302

302-
#ifdef Py_GIL_DISABLED
303-
#define MUTEX_LOCK() PyMutex_Lock(&_PyRuntime.parser.mutex)
304-
#define MUTEX_UNLOCK() PyMutex_Unlock(&_PyRuntime.parser.mutex)
305-
#else
306-
#define MUTEX_LOCK()
307-
#define MUTEX_UNLOCK()
308-
#endif
309-
310303
void
311304
_PyPegen_clear_memo_statistics(void)
312305
{
313-
MUTEX_LOCK();
306+
FT_MUTEX_LOCK(&_PyRuntime.parser.mutex);
314307
for (int i = 0; i < NSTATISTICS; i++) {
315308
memo_statistics[i] = 0;
316309
}
317-
MUTEX_UNLOCK();
310+
FT_MUTEX_UNLOCK(&_PyRuntime.parser.mutex);
318311
}
319312

320313
PyObject *
@@ -325,22 +318,22 @@ _PyPegen_get_memo_statistics(void)
325318
return NULL;
326319
}
327320

328-
MUTEX_LOCK();
321+
FT_MUTEX_LOCK(&_PyRuntime.parser.mutex);
329322
for (int i = 0; i < NSTATISTICS; i++) {
330323
PyObject *value = PyLong_FromLong(memo_statistics[i]);
331324
if (value == NULL) {
332-
MUTEX_UNLOCK();
325+
FT_MUTEX_UNLOCK(&_PyRuntime.parser.mutex);
333326
Py_DECREF(ret);
334327
return NULL;
335328
}
336329
// PyList_SetItem borrows a reference to value.
337330
if (PyList_SetItem(ret, i, value) < 0) {
338-
MUTEX_UNLOCK();
331+
FT_MUTEX_UNLOCK(&_PyRuntime.parser.mutex);
339332
Py_DECREF(ret);
340333
return NULL;
341334
}
342335
}
343-
MUTEX_UNLOCK();
336+
FT_MUTEX_UNLOCK(&_PyRuntime.parser.mutex);
344337
return ret;
345338
}
346339
#endif
@@ -366,9 +359,9 @@ _PyPegen_is_memoized(Parser *p, int type, void *pres)
366359
if (count <= 0) {
367360
count = 1;
368361
}
369-
MUTEX_LOCK();
362+
FT_MUTEX_LOCK(&_PyRuntime.parser.mutex);
370363
memo_statistics[type] += count;
371-
MUTEX_UNLOCK();
364+
FT_MUTEX_UNLOCK(&_PyRuntime.parser.mutex);
372365
}
373366
#endif
374367
p->mark = m->mark;

Python/ceval_gil.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -907,13 +907,9 @@ unsignal_pending_calls(PyThreadState *tstate, PyInterpreterState *interp)
907907
static void
908908
clear_pending_handling_thread(struct _pending_calls *pending)
909909
{
910-
#ifdef Py_GIL_DISABLED
911-
PyMutex_Lock(&pending->mutex);
912-
pending->handling_thread = NULL;
913-
PyMutex_Unlock(&pending->mutex);
914-
#else
910+
FT_MUTEX_LOCK(&pending->mutex);
915911
pending->handling_thread = NULL;
916-
#endif
912+
FT_MUTEX_UNLOCK(&pending->mutex);
917913
}
918914

919915
static int

Python/codecs.c

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Copyright (c) Corporation for National Research Initiatives.
1616
#include "pycore_runtime.h" // _Py_ID()
1717
#include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI
1818
#include "pycore_unicodeobject.h" // _PyUnicode_InternMortal()
19-
19+
#include "pycore_pyatomic_ft_wrappers.h"
2020

2121
static const char *codecs_builtin_error_handlers[] = {
2222
"strict", "ignore", "replace",
@@ -40,13 +40,10 @@ int PyCodec_Register(PyObject *search_function)
4040
PyErr_SetString(PyExc_TypeError, "argument must be callable");
4141
goto onError;
4242
}
43-
#ifdef Py_GIL_DISABLED
44-
PyMutex_Lock(&interp->codecs.search_path_mutex);
45-
#endif
43+
FT_MUTEX_LOCK(&interp->codecs.search_path_mutex);
4644
int ret = PyList_Append(interp->codecs.search_path, search_function);
47-
#ifdef Py_GIL_DISABLED
48-
PyMutex_Unlock(&interp->codecs.search_path_mutex);
49-
#endif
45+
FT_MUTEX_UNLOCK(&interp->codecs.search_path_mutex);
46+
5047
return ret;
5148

5249
onError:
@@ -66,19 +63,15 @@ PyCodec_Unregister(PyObject *search_function)
6663
PyObject *codec_search_path = interp->codecs.search_path;
6764
assert(PyList_CheckExact(codec_search_path));
6865
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(codec_search_path); i++) {
69-
#ifdef Py_GIL_DISABLED
70-
PyMutex_Lock(&interp->codecs.search_path_mutex);
71-
#endif
66+
FT_MUTEX_LOCK(&interp->codecs.search_path_mutex);
7267
PyObject *item = PyList_GetItemRef(codec_search_path, i);
7368
int ret = 1;
7469
if (item == search_function) {
7570
// We hold a reference to the item, so its destructor can't run
7671
// while we hold search_path_mutex.
7772
ret = PyList_SetSlice(codec_search_path, i, i+1, NULL);
7873
}
79-
#ifdef Py_GIL_DISABLED
80-
PyMutex_Unlock(&interp->codecs.search_path_mutex);
81-
#endif
74+
FT_MUTEX_UNLOCK(&interp->codecs.search_path_mutex);
8275
Py_DECREF(item);
8376
if (ret != 1) {
8477
assert(interp->codecs.search_cache != NULL);

Python/legacy_tracing.c

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,6 @@ typedef struct _PyLegacyEventHandler {
2020

2121
#define _PyLegacyEventHandler_CAST(op) ((_PyLegacyEventHandler *)(op))
2222

23-
#ifdef Py_GIL_DISABLED
24-
#define LOCK_SETUP() PyMutex_Lock(&_PyRuntime.ceval.sys_trace_profile_mutex);
25-
#define UNLOCK_SETUP() PyMutex_Unlock(&_PyRuntime.ceval.sys_trace_profile_mutex);
26-
#else
27-
#define LOCK_SETUP()
28-
#define UNLOCK_SETUP()
29-
#endif
3023
/* The Py_tracefunc function expects the following arguments:
3124
* obj: the trace object (PyObject *)
3225
* frame: the current frame (PyFrameObject *)
@@ -509,9 +502,9 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
509502

510503
// needs to be decref'd outside of the lock
511504
PyObject *old_profileobj;
512-
LOCK_SETUP();
505+
FT_MUTEX_LOCK(&_PyRuntime.ceval.sys_trace_profile_mutex);
513506
Py_ssize_t profiling_threads = setup_profile(tstate, func, arg, &old_profileobj);
514-
UNLOCK_SETUP();
507+
FT_MUTEX_UNLOCK(&_PyRuntime.ceval.sys_trace_profile_mutex);
515508
Py_XDECREF(old_profileobj);
516509

517510
uint32_t events = 0;
@@ -605,10 +598,10 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
605598
}
606599
// needs to be decref'd outside of the lock
607600
PyObject *old_traceobj;
608-
LOCK_SETUP();
601+
FT_MUTEX_LOCK(&_PyRuntime.ceval.sys_trace_profile_mutex);
609602
assert(tstate->interp->sys_tracing_threads >= 0);
610603
Py_ssize_t tracing_threads = setup_tracing(tstate, func, arg, &old_traceobj);
611-
UNLOCK_SETUP();
604+
FT_MUTEX_UNLOCK(&_PyRuntime.ceval.sys_trace_profile_mutex);
612605
Py_XDECREF(old_traceobj);
613606
if (tracing_threads < 0) {
614607
return -1;

Python/pystate.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,9 +1689,7 @@ PyThreadState_Clear(PyThreadState *tstate)
16891689
"PyThreadState_Clear: warning: thread still has a generator\n");
16901690
}
16911691

1692-
#ifdef Py_GIL_DISABLED
1693-
PyMutex_Lock(&_PyRuntime.ceval.sys_trace_profile_mutex);
1694-
#endif
1692+
FT_MUTEX_LOCK(&_PyRuntime.ceval.sys_trace_profile_mutex);
16951693

16961694
if (tstate->c_profilefunc != NULL) {
16971695
tstate->interp->sys_profiling_threads--;
@@ -1702,9 +1700,7 @@ PyThreadState_Clear(PyThreadState *tstate)
17021700
tstate->c_tracefunc = NULL;
17031701
}
17041702

1705-
#ifdef Py_GIL_DISABLED
1706-
PyMutex_Unlock(&_PyRuntime.ceval.sys_trace_profile_mutex);
1707-
#endif
1703+
FT_MUTEX_UNLOCK(&_PyRuntime.ceval.sys_trace_profile_mutex);
17081704

17091705
Py_CLEAR(tstate->c_profileobj);
17101706
Py_CLEAR(tstate->c_traceobj);

0 commit comments

Comments
 (0)