gh-112075: Make _PyDict_LoadGlobal thread safe (#117529)

Make _PyDict_LoadGlobal threadsafe
This commit is contained in:
Dino Viehland 2024-04-04 12:26:07 -07:00 committed by GitHub
parent 42205143f8
commit 434bc593df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 23 additions and 29 deletions

View File

@ -97,6 +97,7 @@ extern void _PyDictKeys_DecRef(PyDictKeysObject *keys);
* -1 when no entry found, -3 when compare raises error. * -1 when no entry found, -3 when compare raises error.
*/ */
extern Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); extern Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
extern Py_ssize_t _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *); extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *);
extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key); extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key);

View File

@ -1065,7 +1065,6 @@ compare_unicode_generic(PyDictObject *mp, PyDictKeysObject *dk,
assert(ep->me_key != NULL); assert(ep->me_key != NULL);
assert(PyUnicode_CheckExact(ep->me_key)); assert(PyUnicode_CheckExact(ep->me_key));
assert(!PyUnicode_CheckExact(key)); assert(!PyUnicode_CheckExact(key));
// TODO: Thread safety
if (unicode_get_hash(ep->me_key) == hash) { if (unicode_get_hash(ep->me_key) == hash) {
PyObject *startkey = ep->me_key; PyObject *startkey = ep->me_key;
@ -1192,7 +1191,8 @@ _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **valu
PyDictKeysObject *dk; PyDictKeysObject *dk;
DictKeysKind kind; DictKeysKind kind;
Py_ssize_t ix; Py_ssize_t ix;
// TODO: Thread safety
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp);
start: start:
dk = mp->ma_keys; dk = mp->ma_keys;
kind = dk->dk_kind; kind = dk->dk_kind;
@ -1390,7 +1390,7 @@ dictkeys_generic_lookup_threadsafe(PyDictObject *mp, PyDictKeysObject* dk, PyObj
return do_lookup(mp, dk, key, hash, compare_generic_threadsafe); return do_lookup(mp, dk, key, hash, compare_generic_threadsafe);
} }
static Py_ssize_t Py_ssize_t
_Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr)
{ {
PyDictKeysObject *dk; PyDictKeysObject *dk;
@ -1488,6 +1488,16 @@ read_failed:
return ix; return ix;
} }
#else // Py_GIL_DISABLED
Py_ssize_t
_Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr)
{
Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, value_addr);
Py_XNewRef(*value_addr);
return ix;
}
#endif #endif
int int
@ -2343,11 +2353,12 @@ _PyDict_GetItemStringWithError(PyObject *v, const char *key)
* Raise an exception and return NULL if an error occurred (ex: computing the * Raise an exception and return NULL if an error occurred (ex: computing the
* key hash failed, key comparison failed, ...). Return NULL if the key doesn't * key hash failed, key comparison failed, ...). Return NULL if the key doesn't
* exist. Return the value if the key exists. * exist. Return the value if the key exists.
*
* Returns a new reference.
*/ */
PyObject * PyObject *
_PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
{ {
// TODO: Thread safety
Py_ssize_t ix; Py_ssize_t ix;
Py_hash_t hash; Py_hash_t hash;
PyObject *value; PyObject *value;
@ -2359,14 +2370,14 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
} }
/* namespace 1: globals */ /* namespace 1: globals */
ix = _Py_dict_lookup(globals, key, hash, &value); ix = _Py_dict_lookup_threadsafe(globals, key, hash, &value);
if (ix == DKIX_ERROR) if (ix == DKIX_ERROR)
return NULL; return NULL;
if (ix != DKIX_EMPTY && value != NULL) if (ix != DKIX_EMPTY && value != NULL)
return value; return value;
/* namespace 2: builtins */ /* namespace 2: builtins */
ix = _Py_dict_lookup(builtins, key, hash, &value); ix = _Py_dict_lookup_threadsafe(builtins, key, hash, &value);
assert(ix >= 0 || value == NULL); assert(ix >= 0 || value == NULL);
return value; return value;
} }
@ -3214,11 +3225,7 @@ dict_subscript(PyObject *self, PyObject *key)
if (hash == -1) if (hash == -1)
return NULL; return NULL;
} }
#ifdef Py_GIL_DISABLED
ix = _Py_dict_lookup_threadsafe(mp, key, hash, &value); ix = _Py_dict_lookup_threadsafe(mp, key, hash, &value);
#else
ix = _Py_dict_lookup(mp, key, hash, &value);
#endif
if (ix == DKIX_ERROR) if (ix == DKIX_ERROR)
return NULL; return NULL;
if (ix == DKIX_EMPTY || value == NULL) { if (ix == DKIX_EMPTY || value == NULL) {
@ -3238,11 +3245,7 @@ dict_subscript(PyObject *self, PyObject *key)
_PyErr_SetKeyError(key); _PyErr_SetKeyError(key);
return NULL; return NULL;
} }
#ifdef Py_GIL_DISABLED
return value; return value;
#else
return Py_NewRef(value);
#endif
} }
static int static int
@ -4109,24 +4112,13 @@ dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value)
if (hash == -1) if (hash == -1)
return NULL; return NULL;
} }
#ifdef Py_GIL_DISABLED
ix = _Py_dict_lookup_threadsafe(self, key, hash, &val); ix = _Py_dict_lookup_threadsafe(self, key, hash, &val);
#else
ix = _Py_dict_lookup(self, key, hash, &val);
#endif
if (ix == DKIX_ERROR) if (ix == DKIX_ERROR)
return NULL; return NULL;
#ifdef Py_GIL_DISABLED
if (ix == DKIX_EMPTY || val == NULL) { if (ix == DKIX_EMPTY || val == NULL) {
val = Py_NewRef(default_value); val = Py_NewRef(default_value);
} }
return val; return val;
#else
if (ix == DKIX_EMPTY || val == NULL) {
val = default_value;
}
return Py_NewRef(val);
#endif
} }
static int static int

View File

@ -535,8 +535,12 @@ _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash)
PyObject *value = NULL; PyObject *value = NULL;
PyDictKeysObject *keys = ((PyDictObject *)od)->ma_keys; PyDictKeysObject *keys = ((PyDictObject *)od)->ma_keys;
Py_ssize_t ix; Py_ssize_t ix;
#ifdef Py_GIL_DISABLED
ix = _Py_dict_lookup_threadsafe((PyDictObject *)od, key, hash, &value);
Py_XDECREF(value);
#else
ix = _Py_dict_lookup((PyDictObject *)od, key, hash, &value); ix = _Py_dict_lookup((PyDictObject *)od, key, hash, &value);
#endif
if (ix == DKIX_EMPTY) { if (ix == DKIX_EMPTY) {
return keys->dk_nentries; /* index of new entry */ return keys->dk_nentries; /* index of new entry */
} }

View File

@ -1427,7 +1427,6 @@ dummy_func(
} }
ERROR_IF(true, error); ERROR_IF(true, error);
} }
Py_INCREF(res);
} }
else { else {
/* Slow-path if globals or builtins is not a dict */ /* Slow-path if globals or builtins is not a dict */

View File

@ -1254,7 +1254,6 @@
} }
if (true) JUMP_TO_ERROR(); if (true) JUMP_TO_ERROR();
} }
Py_INCREF(res);
} }
else { else {
/* Slow-path if globals or builtins is not a dict */ /* Slow-path if globals or builtins is not a dict */

View File

@ -4256,7 +4256,6 @@
} }
if (true) goto error; if (true) goto error;
} }
Py_INCREF(res);
} }
else { else {
/* Slow-path if globals or builtins is not a dict */ /* Slow-path if globals or builtins is not a dict */