Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 92abb01

Browse filesBrowse files
authored
gh-112075: Add critical sections for most dict APIs (#114508)
Starts adding thread safety to dict objects. Use @critical_section for APIs which are exposed via argument clinic and don't directly correlate with a public C API which needs to acquire the lock Use a _lock_held suffix for keeping changes to complicated functions simple and just wrapping them with a critical section Acquire and release the lock in an existing function where it won't be overly disruptive to the existing logic
1 parent b6228b5 commit 92abb01
Copy full SHA for 92abb01

File tree

6 files changed

+782
-284
lines changed
Filter options

6 files changed

+782
-284
lines changed

‎Include/internal/pycore_critical_section.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_critical_section.h
+46Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,37 @@ extern "C" {
104104
# define Py_END_CRITICAL_SECTION2() \
105105
_PyCriticalSection2_End(&_cs2); \
106106
}
107+
108+
// Asserts that the mutex is locked. The mutex must be held by the
109+
// top-most critical section otherwise there's the possibility
110+
// that the mutex would be swalled out in some code paths.
111+
#define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex) \
112+
_PyCriticalSection_AssertHeld(mutex)
113+
114+
// Asserts that the mutex for the given object is locked. The mutex must
115+
// be held by the top-most critical section otherwise there's the
116+
// possibility that the mutex would be swalled out in some code paths.
117+
#ifdef Py_DEBUG
118+
119+
#define _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op) \
120+
if (Py_REFCNT(op) != 1) { \
121+
_Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(&_PyObject_CAST(op)->ob_mutex); \
122+
}
123+
124+
#else /* Py_DEBUG */
125+
126+
#define _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op)
127+
128+
#endif /* Py_DEBUG */
129+
107130
#else /* !Py_GIL_DISABLED */
108131
// The critical section APIs are no-ops with the GIL.
109132
# define Py_BEGIN_CRITICAL_SECTION(op)
110133
# define Py_END_CRITICAL_SECTION()
111134
# define Py_BEGIN_CRITICAL_SECTION2(a, b)
112135
# define Py_END_CRITICAL_SECTION2()
136+
# define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex)
137+
# define _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op)
113138
#endif /* !Py_GIL_DISABLED */
114139

115140
typedef struct {
@@ -236,6 +261,27 @@ _PyCriticalSection2_End(_PyCriticalSection2 *c)
236261
PyAPI_FUNC(void)
237262
_PyCriticalSection_SuspendAll(PyThreadState *tstate);
238263

264+
#ifdef Py_GIL_DISABLED
265+
266+
static inline void
267+
_PyCriticalSection_AssertHeld(PyMutex *mutex) {
268+
#ifdef Py_DEBUG
269+
PyThreadState *tstate = _PyThreadState_GET();
270+
uintptr_t prev = tstate->critical_section;
271+
if (prev & _Py_CRITICAL_SECTION_TWO_MUTEXES) {
272+
_PyCriticalSection2 *cs = (_PyCriticalSection2 *)(prev & ~_Py_CRITICAL_SECTION_MASK);
273+
assert(cs != NULL && (cs->base.mutex == mutex || cs->mutex2 == mutex));
274+
}
275+
else {
276+
_PyCriticalSection *cs = (_PyCriticalSection *)(tstate->critical_section & ~_Py_CRITICAL_SECTION_MASK);
277+
assert(cs != NULL && cs->mutex == mutex);
278+
}
279+
280+
#endif
281+
}
282+
283+
#endif
284+
239285
#ifdef __cplusplus
240286
}
241287
#endif

‎Modules/_sre/sre.c

Copy file name to clipboardExpand all lines: Modules/_sre/sre.c
+15-12Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@ static const char copyright[] =
3939
" SRE 2.2.2 Copyright (c) 1997-2002 by Secret Labs AB ";
4040

4141
#include "Python.h"
42-
#include "pycore_dict.h" // _PyDict_Next()
43-
#include "pycore_long.h" // _PyLong_GetZero()
44-
#include "pycore_moduleobject.h" // _PyModule_GetState()
42+
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION
43+
#include "pycore_dict.h" // _PyDict_Next()
44+
#include "pycore_long.h" // _PyLong_GetZero()
45+
#include "pycore_moduleobject.h" // _PyModule_GetState()
4546

46-
#include "sre.h" // SRE_CODE
47+
#include "sre.h" // SRE_CODE
4748

48-
#include <ctype.h> // tolower(), toupper(), isalnum()
49+
#include <ctype.h> // tolower(), toupper(), isalnum()
4950

5051
#define SRE_CODE_BITS (8 * sizeof(SRE_CODE))
5152

@@ -2349,26 +2350,28 @@ _sre_SRE_Match_groupdict_impl(MatchObject *self, PyObject *default_value)
23492350
if (!result || !self->pattern->groupindex)
23502351
return result;
23512352

2353+
Py_BEGIN_CRITICAL_SECTION(self->pattern->groupindex);
23522354
while (_PyDict_Next(self->pattern->groupindex, &pos, &key, &value, &hash)) {
23532355
int status;
23542356
Py_INCREF(key);
23552357
value = match_getslice(self, key, default_value);
23562358
if (!value) {
23572359
Py_DECREF(key);
2358-
goto failed;
2360+
Py_CLEAR(result);
2361+
goto exit;
23592362
}
23602363
status = _PyDict_SetItem_KnownHash(result, key, value, hash);
23612364
Py_DECREF(value);
23622365
Py_DECREF(key);
2363-
if (status < 0)
2364-
goto failed;
2366+
if (status < 0) {
2367+
Py_CLEAR(result);
2368+
goto exit;
2369+
}
23652370
}
2371+
exit:
2372+
Py_END_CRITICAL_SECTION();
23662373

23672374
return result;
2368-
2369-
failed:
2370-
Py_DECREF(result);
2371-
return NULL;
23722375
}
23732376

23742377
/*[clinic input]

‎Objects/clinic/dictobject.c.h

Copy file name to clipboardExpand all lines: Objects/clinic/dictobject.c.h
+28-2Lines changed: 28 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.