Skip to content

Commit 548a7c2

Browse files
committed
Make _PyDict_LoadGlobal threadsafe
1 parent b4fe02f commit 548a7c2

File tree

6 files changed

+27
-10
lines changed

6 files changed

+27
-10
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: 21 additions & 6 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;
@@ -2343,11 +2343,12 @@ _PyDict_GetItemStringWithError(PyObject *v, const char *key)
23432343
* Raise an exception and return NULL if an error occurred (ex: computing the
23442344
* key hash failed, key comparison failed, ...). Return NULL if the key doesn't
23452345
* exist. Return the value if the key exists.
2346+
*
2347+
* Returns a new reference.
23462348
*/
23472349
PyObject *
23482350
_PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
23492351
{
2350-
// TODO: Thread safety
23512352
Py_ssize_t ix;
23522353
Py_hash_t hash;
23532354
PyObject *value;
@@ -2358,17 +2359,31 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
23582359
return NULL;
23592360
}
23602361

2362+
#ifdef Py_GIL_DISABLED
23612363
/* namespace 1: globals */
2362-
ix = _Py_dict_lookup(globals, key, hash, &value);
2364+
ix = _Py_dict_lookup_threadsafe(globals, key, hash, &value);
23632365
if (ix == DKIX_ERROR)
23642366
return NULL;
23652367
if (ix != DKIX_EMPTY && value != NULL)
23662368
return value;
23672369

23682370
/* namespace 2: builtins */
2369-
ix = _Py_dict_lookup(builtins, key, hash, &value);
2371+
ix = _Py_dict_lookup_threadsafe(builtins, key, hash, &value);
23702372
assert(ix >= 0 || value == NULL);
23712373
return value;
2374+
#else
2375+
/* namespace 1: globals */
2376+
ix = _Py_dict_lookup(globals, key, hash, &value);
2377+
if (ix == DKIX_ERROR)
2378+
return NULL;
2379+
if (ix != DKIX_EMPTY && value != NULL)
2380+
return Py_NewRef(value);
2381+
2382+
/* namespace 2: builtins */
2383+
ix = _Py_dict_lookup(builtins, key, hash, &value);
2384+
assert(ix >= 0 || value == NULL);
2385+
return Py_XNewRef(value);
2386+
#endif
23722387
}
23732388

23742389
/* Consumes references to key and value */

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
@@ -1426,7 +1426,6 @@ dummy_func(
14261426
}
14271427
ERROR_IF(true, error);
14281428
}
1429-
Py_INCREF(res);
14301429
}
14311430
else {
14321431
/* 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)