Skip to content

Commit 434bc59

Browse files
authored
gh-112075: Make _PyDict_LoadGlobal thread safe (#117529)
Make _PyDict_LoadGlobal threadsafe
1 parent 4220514 commit 434bc59

File tree

6 files changed

+23
-29
lines changed

6 files changed

+23
-29
lines changed

Include/internal/pycore_dict.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ extern void _PyDictKeys_DecRef(PyDictKeysObject *keys);
9797
* -1 when no entry found, -3 when compare raises error.
9898
*/
9999
extern Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
100+
extern Py_ssize_t _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
100101

101102
extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *);
102103
extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key);

Objects/dictobject.c

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,7 +1065,6 @@ compare_unicode_generic(PyDictObject *mp, PyDictKeysObject *dk,
10651065
assert(ep->me_key != NULL);
10661066
assert(PyUnicode_CheckExact(ep->me_key));
10671067
assert(!PyUnicode_CheckExact(key));
1068-
// TODO: Thread safety
10691068

10701069
if (unicode_get_hash(ep->me_key) == hash) {
10711070
PyObject *startkey = ep->me_key;
@@ -1192,7 +1191,8 @@ _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **valu
11921191
PyDictKeysObject *dk;
11931192
DictKeysKind kind;
11941193
Py_ssize_t ix;
1195-
// TODO: Thread safety
1194+
1195+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp);
11961196
start:
11971197
dk = mp->ma_keys;
11981198
kind = dk->dk_kind;
@@ -1390,7 +1390,7 @@ dictkeys_generic_lookup_threadsafe(PyDictObject *mp, PyDictKeysObject* dk, PyObj
13901390
return do_lookup(mp, dk, key, hash, compare_generic_threadsafe);
13911391
}
13921392

1393-
static Py_ssize_t
1393+
Py_ssize_t
13941394
_Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr)
13951395
{
13961396
PyDictKeysObject *dk;
@@ -1488,6 +1488,16 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb
14881488
return ix;
14891489
}
14901490

1491+
#else // Py_GIL_DISABLED
1492+
1493+
Py_ssize_t
1494+
_Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr)
1495+
{
1496+
Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, value_addr);
1497+
Py_XNewRef(*value_addr);
1498+
return ix;
1499+
}
1500+
14911501
#endif
14921502

14931503
int
@@ -2343,11 +2353,12 @@ _PyDict_GetItemStringWithError(PyObject *v, const char *key)
23432353
* Raise an exception and return NULL if an error occurred (ex: computing the
23442354
* key hash failed, key comparison failed, ...). Return NULL if the key doesn't
23452355
* exist. Return the value if the key exists.
2356+
*
2357+
* Returns a new reference.
23462358
*/
23472359
PyObject *
23482360
_PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
23492361
{
2350-
// TODO: Thread safety
23512362
Py_ssize_t ix;
23522363
Py_hash_t hash;
23532364
PyObject *value;
@@ -2359,14 +2370,14 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
23592370
}
23602371

23612372
/* namespace 1: globals */
2362-
ix = _Py_dict_lookup(globals, key, hash, &value);
2373+
ix = _Py_dict_lookup_threadsafe(globals, key, hash, &value);
23632374
if (ix == DKIX_ERROR)
23642375
return NULL;
23652376
if (ix != DKIX_EMPTY && value != NULL)
23662377
return value;
23672378

23682379
/* namespace 2: builtins */
2369-
ix = _Py_dict_lookup(builtins, key, hash, &value);
2380+
ix = _Py_dict_lookup_threadsafe(builtins, key, hash, &value);
23702381
assert(ix >= 0 || value == NULL);
23712382
return value;
23722383
}
@@ -3214,11 +3225,7 @@ dict_subscript(PyObject *self, PyObject *key)
32143225
if (hash == -1)
32153226
return NULL;
32163227
}
3217-
#ifdef Py_GIL_DISABLED
32183228
ix = _Py_dict_lookup_threadsafe(mp, key, hash, &value);
3219-
#else
3220-
ix = _Py_dict_lookup(mp, key, hash, &value);
3221-
#endif
32223229
if (ix == DKIX_ERROR)
32233230
return NULL;
32243231
if (ix == DKIX_EMPTY || value == NULL) {
@@ -3238,11 +3245,7 @@ dict_subscript(PyObject *self, PyObject *key)
32383245
_PyErr_SetKeyError(key);
32393246
return NULL;
32403247
}
3241-
#ifdef Py_GIL_DISABLED
32423248
return value;
3243-
#else
3244-
return Py_NewRef(value);
3245-
#endif
32463249
}
32473250

32483251
static int
@@ -4109,24 +4112,13 @@ dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value)
41094112
if (hash == -1)
41104113
return NULL;
41114114
}
4112-
#ifdef Py_GIL_DISABLED
41134115
ix = _Py_dict_lookup_threadsafe(self, key, hash, &val);
4114-
#else
4115-
ix = _Py_dict_lookup(self, key, hash, &val);
4116-
#endif
41174116
if (ix == DKIX_ERROR)
41184117
return NULL;
4119-
#ifdef Py_GIL_DISABLED
41204118
if (ix == DKIX_EMPTY || val == NULL) {
41214119
val = Py_NewRef(default_value);
41224120
}
41234121
return val;
4124-
#else
4125-
if (ix == DKIX_EMPTY || val == NULL) {
4126-
val = default_value;
4127-
}
4128-
return Py_NewRef(val);
4129-
#endif
41304122
}
41314123

41324124
static int

Objects/odictobject.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,8 +535,12 @@ _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash)
535535
PyObject *value = NULL;
536536
PyDictKeysObject *keys = ((PyDictObject *)od)->ma_keys;
537537
Py_ssize_t ix;
538-
538+
#ifdef Py_GIL_DISABLED
539+
ix = _Py_dict_lookup_threadsafe((PyDictObject *)od, key, hash, &value);
540+
Py_XDECREF(value);
541+
#else
539542
ix = _Py_dict_lookup((PyDictObject *)od, key, hash, &value);
543+
#endif
540544
if (ix == DKIX_EMPTY) {
541545
return keys->dk_nentries; /* index of new entry */
542546
}

Python/bytecodes.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1427,7 +1427,6 @@ dummy_func(
14271427
}
14281428
ERROR_IF(true, error);
14291429
}
1430-
Py_INCREF(res);
14311430
}
14321431
else {
14331432
/* Slow-path if globals or builtins is not a dict */

Python/executor_cases.c.h

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)