bpo-40839: PyDict_GetItem() requires the GIL (GH-20580)

Calling PyDict_GetItem() without GIL held had been allowed for
historical reason. It is no longer allowed.
This commit is contained in:
Victor Stinner 2020-06-02 14:03:25 +02:00 committed by GitHub
parent 85339f5c22
commit 59d3dce69b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 30 deletions

View file

@ -100,6 +100,10 @@ Dictionary Objects
:meth:`__eq__` methods will get suppressed. :meth:`__eq__` methods will get suppressed.
To get error reporting use :c:func:`PyDict_GetItemWithError()` instead. To get error reporting use :c:func:`PyDict_GetItemWithError()` instead.
.. versionchanged:: 3.10
Calling this API without :term:`GIL` held had been allowed for historical
reason. It is no longer allowed.
.. c:function:: PyObject* PyDict_GetItemWithError(PyObject *p, PyObject *key) .. c:function:: PyObject* PyDict_GetItemWithError(PyObject *p, PyObject *key)

View file

@ -148,5 +148,9 @@ Porting to Python 3.10
see :c:func:`Py_SET_SIZE()` (available since Python 3.9). see :c:func:`Py_SET_SIZE()` (available since Python 3.9).
(Contributed by Victor Stinner in :issue:`39573`.) (Contributed by Victor Stinner in :issue:`39573`.)
* Calling :c:func:`PyDict_GetItem` without :term:`GIL` held had been allowed
for historical reason. It is no longer allowed.
(Contributed by Victor Stinner in :issue:`40839`.)
Removed Removed
------- -------

View file

@ -0,0 +1,2 @@
Calling :c:func:`PyDict_GetItem` without :term:`GIL` held had been allowed for
historical reason. It is no longer allowed.

View file

@ -112,7 +112,8 @@ converting the dict to the combined table.
#include "Python.h" #include "Python.h"
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() #include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
#include "pycore_object.h" #include "pycore_object.h" // _PyObject_GC_TRACK()
#include "pycore_pyerrors.h" // _PyErr_Fetch()
#include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_pystate.h" // _PyThreadState_GET()
#include "dict-common.h" #include "dict-common.h"
#include "stringlib/eq.h" // unicode_eq() #include "stringlib/eq.h" // unicode_eq()
@ -1387,14 +1388,12 @@ _PyDict_NewPresized(Py_ssize_t minused)
PyObject * PyObject *
PyDict_GetItem(PyObject *op, PyObject *key) PyDict_GetItem(PyObject *op, PyObject *key)
{ {
Py_hash_t hash; if (!PyDict_Check(op)) {
Py_ssize_t ix;
PyDictObject *mp = (PyDictObject *)op;
PyThreadState *tstate;
PyObject *value;
if (!PyDict_Check(op))
return NULL; return NULL;
}
PyDictObject *mp = (PyDictObject *)op;
Py_hash_t hash;
if (!PyUnicode_CheckExact(key) || if (!PyUnicode_CheckExact(key) ||
(hash = ((PyASCIIObject *) key)->hash) == -1) (hash = ((PyASCIIObject *) key)->hash) == -1)
{ {
@ -1405,29 +1404,27 @@ PyDict_GetItem(PyObject *op, PyObject *key)
} }
} }
/* We can arrive here with a NULL tstate during initialization: try PyThreadState *tstate = _PyThreadState_GET();
running "python -Wi" for an example related to string interning. #ifdef Py_DEBUG
Let's just hope that no exception occurs then... This must be // bpo-40839: Before Python 3.10, it was possible to call PyDict_GetItem()
_PyThreadState_GET() and not PyThreadState_Get() because the latter // with the GIL released.
abort Python if tstate is NULL. */ _Py_EnsureTstateNotNULL(tstate);
tstate = _PyThreadState_GET(); #endif
if (tstate != NULL && tstate->curexc_type != NULL) {
/* preserve the existing exception */ /* Preserve the existing exception */
PyObject *err_type, *err_value, *err_tb; PyObject *exc_type, *exc_value, *exc_tb;
PyErr_Fetch(&err_type, &err_value, &err_tb); PyObject *value;
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); Py_ssize_t ix;
/* ignore errors */
PyErr_Restore(err_type, err_value, err_tb); _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb);
if (ix < 0)
return NULL;
}
else {
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value);
/* Ignore any exception raised by the lookup */
_PyErr_Restore(tstate, exc_type, exc_value, exc_tb);
if (ix < 0) { if (ix < 0) {
PyErr_Clear();
return NULL; return NULL;
} }
}
return value; return value;
} }