mirror of
https://github.com/python/cpython.git
synced 2025-11-24 12:20:42 +00:00
gh-125996: fix thread safety of collections.OrderedDict (#133734)
Some checks are pending
Tests / (push) Blocked by required conditions
Tests / Windows MSI (push) Blocked by required conditions
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Sanitizers (push) Blocked by required conditions
Tests / Ubuntu SSL tests with AWS-LC (push) Blocked by required conditions
Tests / Android (aarch64) (push) Blocked by required conditions
Tests / Android (x86_64) (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
mypy / Run mypy on Lib/_pyrepl (push) Waiting to run
mypy / Run mypy on Lib/test/libregrtest (push) Waiting to run
mypy / Run mypy on Lib/tomllib (push) Waiting to run
mypy / Run mypy on Tools/build (push) Waiting to run
mypy / Run mypy on Tools/cases_generator (push) Waiting to run
mypy / Run mypy on Tools/clinic (push) Waiting to run
mypy / Run mypy on Tools/jit (push) Waiting to run
mypy / Run mypy on Tools/peg_generator (push) Waiting to run
Some checks are pending
Tests / (push) Blocked by required conditions
Tests / Windows MSI (push) Blocked by required conditions
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Sanitizers (push) Blocked by required conditions
Tests / Ubuntu SSL tests with AWS-LC (push) Blocked by required conditions
Tests / Android (aarch64) (push) Blocked by required conditions
Tests / Android (x86_64) (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
mypy / Run mypy on Lib/_pyrepl (push) Waiting to run
mypy / Run mypy on Lib/test/libregrtest (push) Waiting to run
mypy / Run mypy on Lib/tomllib (push) Waiting to run
mypy / Run mypy on Tools/build (push) Waiting to run
mypy / Run mypy on Tools/cases_generator (push) Waiting to run
mypy / Run mypy on Tools/clinic (push) Waiting to run
mypy / Run mypy on Tools/jit (push) Waiting to run
mypy / Run mypy on Tools/peg_generator (push) Waiting to run
This commit is contained in:
parent
525dcfe523
commit
6481539a6d
5 changed files with 266 additions and 76 deletions
|
|
@ -30,6 +30,10 @@ PyAPI_FUNC(int) _PyDict_SetItem_KnownHash(PyObject *mp, PyObject *key,
|
|||
// Export for '_asyncio' shared extension
|
||||
PyAPI_FUNC(int) _PyDict_DelItem_KnownHash(PyObject *mp, PyObject *key,
|
||||
Py_hash_t hash);
|
||||
|
||||
extern int _PyDict_DelItem_KnownHash_LockHeld(PyObject *mp, PyObject *key,
|
||||
Py_hash_t hash);
|
||||
|
||||
extern int _PyDict_Contains_KnownHash(PyObject *, PyObject *, Py_hash_t);
|
||||
|
||||
// "Id" variants
|
||||
|
|
@ -47,6 +51,8 @@ extern int _PyDict_HasOnlyStringKeys(PyObject *mp);
|
|||
// Export for '_ctypes' shared extension
|
||||
PyAPI_FUNC(Py_ssize_t) _PyDict_SizeOf(PyDictObject *);
|
||||
|
||||
extern Py_ssize_t _PyDict_SizeOf_LockHeld(PyDictObject *);
|
||||
|
||||
#define _PyDict_HasSplitTable(d) ((d)->ma_values != NULL)
|
||||
|
||||
/* Like PyDict_Merge, but override can be 0, 1 or 2. If override is 0,
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
Fix thread safety of :class:`collections.OrderedDict`. Patch by Kumar Aditya.
|
||||
106
Objects/clinic/odictobject.c.h
generated
106
Objects/clinic/odictobject.c.h
generated
|
|
@ -6,6 +6,7 @@ preserve
|
|||
# include "pycore_gc.h" // PyGC_Head
|
||||
# include "pycore_runtime.h" // _Py_ID()
|
||||
#endif
|
||||
#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
|
||||
#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
|
||||
|
||||
PyDoc_STRVAR(OrderedDict_fromkeys__doc__,
|
||||
|
|
@ -73,6 +74,53 @@ exit:
|
|||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(OrderedDict___sizeof____doc__,
|
||||
"__sizeof__($self, /)\n"
|
||||
"--\n"
|
||||
"\n");
|
||||
|
||||
#define ORDEREDDICT___SIZEOF___METHODDEF \
|
||||
{"__sizeof__", (PyCFunction)OrderedDict___sizeof__, METH_NOARGS, OrderedDict___sizeof____doc__},
|
||||
|
||||
static Py_ssize_t
|
||||
OrderedDict___sizeof___impl(PyODictObject *self);
|
||||
|
||||
static PyObject *
|
||||
OrderedDict___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
Py_ssize_t _return_value;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
_return_value = OrderedDict___sizeof___impl((PyODictObject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
if ((_return_value == -1) && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
return_value = PyLong_FromSsize_t(_return_value);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(OrderedDict___reduce____doc__,
|
||||
"__reduce__($self, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Return state information for pickling");
|
||||
|
||||
#define ORDEREDDICT___REDUCE___METHODDEF \
|
||||
{"__reduce__", (PyCFunction)OrderedDict___reduce__, METH_NOARGS, OrderedDict___reduce____doc__},
|
||||
|
||||
static PyObject *
|
||||
OrderedDict___reduce___impl(PyODictObject *od);
|
||||
|
||||
static PyObject *
|
||||
OrderedDict___reduce__(PyObject *od, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return OrderedDict___reduce___impl((PyODictObject *)od);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(OrderedDict_setdefault__doc__,
|
||||
"setdefault($self, /, key, default=None)\n"
|
||||
"--\n"
|
||||
|
|
@ -135,7 +183,9 @@ OrderedDict_setdefault(PyObject *self, PyObject *const *args, Py_ssize_t nargs,
|
|||
}
|
||||
default_value = args[1];
|
||||
skip_optional_pos:
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = OrderedDict_setdefault_impl((PyODictObject *)self, key, default_value);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
|
@ -204,7 +254,9 @@ OrderedDict_pop(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObjec
|
|||
}
|
||||
default_value = args[1];
|
||||
skip_optional_pos:
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = OrderedDict_pop_impl((PyODictObject *)self, key, default_value);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
|
@ -272,12 +324,62 @@ OrderedDict_popitem(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyO
|
|||
goto exit;
|
||||
}
|
||||
skip_optional_pos:
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = OrderedDict_popitem_impl((PyODictObject *)self, last);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(OrderedDict_clear__doc__,
|
||||
"clear($self, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Remove all items from ordered dict.");
|
||||
|
||||
#define ORDEREDDICT_CLEAR_METHODDEF \
|
||||
{"clear", (PyCFunction)OrderedDict_clear, METH_NOARGS, OrderedDict_clear__doc__},
|
||||
|
||||
static PyObject *
|
||||
OrderedDict_clear_impl(PyODictObject *self);
|
||||
|
||||
static PyObject *
|
||||
OrderedDict_clear(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = OrderedDict_clear_impl((PyODictObject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(OrderedDict_copy__doc__,
|
||||
"copy($self, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"A shallow copy of ordered dict.");
|
||||
|
||||
#define ORDEREDDICT_COPY_METHODDEF \
|
||||
{"copy", (PyCFunction)OrderedDict_copy, METH_NOARGS, OrderedDict_copy__doc__},
|
||||
|
||||
static PyObject *
|
||||
OrderedDict_copy_impl(PyObject *od);
|
||||
|
||||
static PyObject *
|
||||
OrderedDict_copy(PyObject *od, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(od);
|
||||
return_value = OrderedDict_copy_impl(od);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(OrderedDict_move_to_end__doc__,
|
||||
"move_to_end($self, /, key, last=True)\n"
|
||||
"--\n"
|
||||
|
|
@ -342,9 +444,11 @@ OrderedDict_move_to_end(PyObject *self, PyObject *const *args, Py_ssize_t nargs,
|
|||
goto exit;
|
||||
}
|
||||
skip_optional_pos:
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = OrderedDict_move_to_end_impl((PyODictObject *)self, key, last);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=7d8206823bb1f419 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=7bc997ca7900f06f input=a9049054013a1b77]*/
|
||||
|
|
|
|||
|
|
@ -2819,8 +2819,8 @@ PyDict_DelItem(PyObject *op, PyObject *key)
|
|||
return _PyDict_DelItem_KnownHash(op, key, hash);
|
||||
}
|
||||
|
||||
static int
|
||||
delitem_knownhash_lock_held(PyObject *op, PyObject *key, Py_hash_t hash)
|
||||
int
|
||||
_PyDict_DelItem_KnownHash_LockHeld(PyObject *op, PyObject *key, Py_hash_t hash)
|
||||
{
|
||||
Py_ssize_t ix;
|
||||
PyDictObject *mp;
|
||||
|
|
@ -2855,7 +2855,7 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
|
|||
{
|
||||
int res;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
res = delitem_knownhash_lock_held(op, key, hash);
|
||||
res = _PyDict_DelItem_KnownHash_LockHeld(op, key, hash);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
return res;
|
||||
}
|
||||
|
|
@ -4703,9 +4703,11 @@ dict_tp_clear(PyObject *op)
|
|||
|
||||
static PyObject *dictiter_new(PyDictObject *, PyTypeObject *);
|
||||
|
||||
static Py_ssize_t
|
||||
sizeof_lock_held(PyDictObject *mp)
|
||||
Py_ssize_t
|
||||
_PyDict_SizeOf_LockHeld(PyDictObject *mp)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp);
|
||||
|
||||
size_t res = _PyObject_SIZE(Py_TYPE(mp));
|
||||
if (_PyDict_HasSplitTable(mp)) {
|
||||
res += shared_keys_usable_size(mp->ma_keys) * sizeof(PyObject*);
|
||||
|
|
@ -4724,7 +4726,7 @@ _PyDict_SizeOf(PyDictObject *mp)
|
|||
{
|
||||
Py_ssize_t res;
|
||||
Py_BEGIN_CRITICAL_SECTION(mp);
|
||||
res = sizeof_lock_held(mp);
|
||||
res = _PyDict_SizeOf_LockHeld(mp);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return res;
|
||||
|
|
@ -6909,7 +6911,7 @@ _PyDict_SetItem_LockHeld(PyDictObject *dict, PyObject *name, PyObject *value)
|
|||
dict_unhashable_type(name);
|
||||
return -1;
|
||||
}
|
||||
return delitem_knownhash_lock_held((PyObject *)dict, name, hash);
|
||||
return _PyDict_DelItem_KnownHash_LockHeld((PyObject *)dict, name, hash);
|
||||
} else {
|
||||
return setitem_lock_held(dict, name, value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -536,6 +536,7 @@ struct _odictnode {
|
|||
static Py_ssize_t
|
||||
_odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
PyObject *value = NULL;
|
||||
PyDictKeysObject *keys = ((PyDictObject *)od)->ma_keys;
|
||||
Py_ssize_t ix;
|
||||
|
|
@ -560,6 +561,7 @@ _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash)
|
|||
static int
|
||||
_odict_resize(PyODictObject *od)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
Py_ssize_t size, i;
|
||||
_ODictNode **fast_nodes, *node;
|
||||
|
||||
|
|
@ -596,6 +598,7 @@ _odict_resize(PyODictObject *od)
|
|||
static Py_ssize_t
|
||||
_odict_get_index(PyODictObject *od, PyObject *key, Py_hash_t hash)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
PyDictKeysObject *keys;
|
||||
|
||||
assert(key != NULL);
|
||||
|
|
@ -616,6 +619,7 @@ _odict_get_index(PyODictObject *od, PyObject *key, Py_hash_t hash)
|
|||
static _ODictNode *
|
||||
_odict_find_node_hash(PyODictObject *od, PyObject *key, Py_hash_t hash)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
Py_ssize_t index;
|
||||
|
||||
if (_odict_EMPTY(od))
|
||||
|
|
@ -630,6 +634,7 @@ _odict_find_node_hash(PyODictObject *od, PyObject *key, Py_hash_t hash)
|
|||
static _ODictNode *
|
||||
_odict_find_node(PyODictObject *od, PyObject *key)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
Py_ssize_t index;
|
||||
Py_hash_t hash;
|
||||
|
||||
|
|
@ -648,6 +653,7 @@ _odict_find_node(PyODictObject *od, PyObject *key)
|
|||
static void
|
||||
_odict_add_head(PyODictObject *od, _ODictNode *node)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
_odictnode_PREV(node) = NULL;
|
||||
_odictnode_NEXT(node) = _odict_FIRST(od);
|
||||
if (_odict_FIRST(od) == NULL)
|
||||
|
|
@ -661,6 +667,7 @@ _odict_add_head(PyODictObject *od, _ODictNode *node)
|
|||
static void
|
||||
_odict_add_tail(PyODictObject *od, _ODictNode *node)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
_odictnode_PREV(node) = _odict_LAST(od);
|
||||
_odictnode_NEXT(node) = NULL;
|
||||
if (_odict_LAST(od) == NULL)
|
||||
|
|
@ -675,6 +682,7 @@ _odict_add_tail(PyODictObject *od, _ODictNode *node)
|
|||
static int
|
||||
_odict_add_new_node(PyODictObject *od, PyObject *key, Py_hash_t hash)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
Py_ssize_t i;
|
||||
_ODictNode *node;
|
||||
|
||||
|
|
@ -719,6 +727,7 @@ _odict_add_new_node(PyODictObject *od, PyObject *key, Py_hash_t hash)
|
|||
static void
|
||||
_odict_remove_node(PyODictObject *od, _ODictNode *node)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
if (_odict_FIRST(od) == node)
|
||||
_odict_FIRST(od) = _odictnode_NEXT(node);
|
||||
else if (_odictnode_PREV(node) != NULL)
|
||||
|
|
@ -754,6 +763,7 @@ static int
|
|||
_odict_clear_node(PyODictObject *od, _ODictNode *node, PyObject *key,
|
||||
Py_hash_t hash)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
Py_ssize_t i;
|
||||
|
||||
assert(key != NULL);
|
||||
|
|
@ -952,31 +962,34 @@ OrderedDict_fromkeys_impl(PyTypeObject *type, PyObject *seq, PyObject *value)
|
|||
return _PyDict_FromKeys((PyObject *)type, seq, value);
|
||||
}
|
||||
|
||||
/* __sizeof__() */
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
OrderedDict.__sizeof__ -> Py_ssize_t
|
||||
[clinic start generated code]*/
|
||||
|
||||
/* OrderedDict.__sizeof__() does not have a docstring. */
|
||||
PyDoc_STRVAR(odict_sizeof__doc__, "");
|
||||
|
||||
static PyObject *
|
||||
odict_sizeof(PyObject *op, PyObject *Py_UNUSED(ignored))
|
||||
static Py_ssize_t
|
||||
OrderedDict___sizeof___impl(PyODictObject *self)
|
||||
/*[clinic end generated code: output=1a8560db8cf83ac5 input=655e989ae24daa6a]*/
|
||||
{
|
||||
PyODictObject *od = _PyODictObject_CAST(op);
|
||||
Py_ssize_t res = _PyDict_SizeOf((PyDictObject *)od);
|
||||
res += sizeof(_ODictNode *) * od->od_fast_nodes_size; /* od_fast_nodes */
|
||||
if (!_odict_EMPTY(od)) {
|
||||
res += sizeof(_ODictNode) * PyODict_SIZE(od); /* linked-list */
|
||||
Py_ssize_t res = _PyDict_SizeOf_LockHeld((PyDictObject *)self);
|
||||
res += sizeof(_ODictNode *) * self->od_fast_nodes_size; /* od_fast_nodes */
|
||||
if (!_odict_EMPTY(self)) {
|
||||
res += sizeof(_ODictNode) * PyODict_SIZE(self); /* linked-list */
|
||||
}
|
||||
return PyLong_FromSsize_t(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* __reduce__() */
|
||||
/*[clinic input]
|
||||
OrderedDict.__reduce__
|
||||
self as od: self(type="PyODictObject *")
|
||||
|
||||
PyDoc_STRVAR(odict_reduce__doc__, "Return state information for pickling");
|
||||
Return state information for pickling
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
odict_reduce(PyObject *op, PyObject *Py_UNUSED(ignored))
|
||||
OrderedDict___reduce___impl(PyODictObject *od)
|
||||
/*[clinic end generated code: output=71eeb81f760a6a8e input=b0467c7ec400fe5e]*/
|
||||
{
|
||||
register PyODictObject *od = _PyODictObject_CAST(op);
|
||||
PyObject *state, *result = NULL;
|
||||
PyObject *items_iter, *items, *args = NULL;
|
||||
|
||||
|
|
@ -1011,8 +1024,10 @@ Done:
|
|||
|
||||
/* setdefault(): Skips __missing__() calls. */
|
||||
|
||||
static int PyODict_SetItem_LockHeld(PyObject *self, PyObject *key, PyObject *value);
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
OrderedDict.setdefault
|
||||
|
||||
key: object
|
||||
|
|
@ -1026,7 +1041,7 @@ Return the value for key if key is in the dictionary, else default.
|
|||
static PyObject *
|
||||
OrderedDict_setdefault_impl(PyODictObject *self, PyObject *key,
|
||||
PyObject *default_value)
|
||||
/*[clinic end generated code: output=97537cb7c28464b6 input=38e098381c1efbc6]*/
|
||||
/*[clinic end generated code: output=97537cb7c28464b6 input=d7b93e92734f99b5]*/
|
||||
{
|
||||
PyObject *result = NULL;
|
||||
|
||||
|
|
@ -1036,7 +1051,7 @@ OrderedDict_setdefault_impl(PyODictObject *self, PyObject *key,
|
|||
if (PyErr_Occurred())
|
||||
return NULL;
|
||||
assert(_odict_find_node(self, key) == NULL);
|
||||
if (PyODict_SetItem((PyObject *)self, key, default_value) >= 0) {
|
||||
if (PyODict_SetItem_LockHeld((PyObject *)self, key, default_value) >= 0) {
|
||||
result = Py_NewRef(default_value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1066,10 +1081,9 @@ static PyObject *
|
|||
_odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj,
|
||||
Py_hash_t hash)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
|
||||
PyObject *value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(od);
|
||||
|
||||
_ODictNode *node = _odict_find_node_hash(_PyODictObject_CAST(od), key, hash);
|
||||
if (node != NULL) {
|
||||
/* Pop the node first to avoid a possible dict resize (due to
|
||||
|
|
@ -1094,7 +1108,6 @@ _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj,
|
|||
PyErr_SetObject(PyExc_KeyError, key);
|
||||
}
|
||||
}
|
||||
Py_END_CRITICAL_SECTION();
|
||||
done:
|
||||
|
||||
return value;
|
||||
|
|
@ -1102,6 +1115,7 @@ done:
|
|||
|
||||
/* Skips __missing__() calls. */
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
@permit_long_summary
|
||||
OrderedDict.pop
|
||||
|
||||
|
|
@ -1117,7 +1131,7 @@ raise a KeyError.
|
|||
static PyObject *
|
||||
OrderedDict_pop_impl(PyODictObject *self, PyObject *key,
|
||||
PyObject *default_value)
|
||||
/*[clinic end generated code: output=7a6447d104e7494b input=eebd40ac51666d33]*/
|
||||
/*[clinic end generated code: output=7a6447d104e7494b input=0742e3c9bf076a72]*/
|
||||
{
|
||||
Py_hash_t hash = PyObject_Hash(key);
|
||||
if (hash == -1)
|
||||
|
|
@ -1129,6 +1143,7 @@ OrderedDict_pop_impl(PyODictObject *self, PyObject *key,
|
|||
/* popitem() */
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
OrderedDict.popitem
|
||||
|
||||
last: bool = True
|
||||
|
|
@ -1140,7 +1155,7 @@ Pairs are returned in LIFO order if last is true or FIFO order if false.
|
|||
|
||||
static PyObject *
|
||||
OrderedDict_popitem_impl(PyODictObject *self, int last)
|
||||
/*[clinic end generated code: output=98e7d986690d49eb input=d992ac5ee8305e1a]*/
|
||||
/*[clinic end generated code: output=98e7d986690d49eb input=8aafc7433e0a40e7]*/
|
||||
{
|
||||
PyObject *key, *value, *item = NULL;
|
||||
_ODictNode *node;
|
||||
|
|
@ -1169,6 +1184,9 @@ OrderedDict_popitem_impl(PyODictObject *self, int last)
|
|||
PyDoc_STRVAR(odict_keys__doc__, "");
|
||||
|
||||
static PyObject * odictkeys_new(PyObject *od, PyObject *Py_UNUSED(ignored)); /* forward */
|
||||
static int
|
||||
_PyODict_SetItem_KnownHash_LockHeld(PyObject *od, PyObject *key, PyObject *value,
|
||||
Py_hash_t hash); /* forward */
|
||||
|
||||
/* values() */
|
||||
|
||||
|
|
@ -1194,32 +1212,36 @@ static PyObject * mutablemapping_update(PyObject *, PyObject *, PyObject *);
|
|||
|
||||
#define odict_update mutablemapping_update
|
||||
|
||||
/* clear() */
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
OrderedDict.clear
|
||||
|
||||
PyDoc_STRVAR(odict_clear__doc__,
|
||||
"od.clear() -> None. Remove all items from od.");
|
||||
Remove all items from ordered dict.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
odict_clear(PyObject *op, PyObject *Py_UNUSED(ignored))
|
||||
OrderedDict_clear_impl(PyODictObject *self)
|
||||
/*[clinic end generated code: output=a1a76d1322f556c5 input=08b12322e74c535c]*/
|
||||
{
|
||||
register PyODictObject *od = _PyODictObject_CAST(op);
|
||||
PyDict_Clear(op);
|
||||
_odict_clear_nodes(od);
|
||||
_PyDict_Clear_LockHeld((PyObject *)self);
|
||||
_odict_clear_nodes(self);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/* copy() */
|
||||
|
||||
/* forward */
|
||||
static int _PyODict_SetItem_KnownHash(PyObject *, PyObject *, PyObject *,
|
||||
Py_hash_t);
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
OrderedDict.copy
|
||||
self as od: self
|
||||
|
||||
PyDoc_STRVAR(odict_copy__doc__, "od.copy() -> a shallow copy of od");
|
||||
A shallow copy of ordered dict.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
odict_copy(PyObject *op, PyObject *Py_UNUSED(ignored))
|
||||
OrderedDict_copy_impl(PyObject *od)
|
||||
/*[clinic end generated code: output=9cdbe7394aecc576 input=e329951ae617ed48]*/
|
||||
{
|
||||
register PyODictObject *od = _PyODictObject_CAST(op);
|
||||
_ODictNode *node;
|
||||
PyObject *od_copy;
|
||||
|
||||
|
|
@ -1239,8 +1261,8 @@ odict_copy(PyObject *op, PyObject *Py_UNUSED(ignored))
|
|||
PyErr_SetObject(PyExc_KeyError, key);
|
||||
goto fail;
|
||||
}
|
||||
if (_PyODict_SetItem_KnownHash((PyObject *)od_copy, key, value,
|
||||
_odictnode_HASH(node)) != 0)
|
||||
if (_PyODict_SetItem_KnownHash_LockHeld((PyObject *)od_copy, key, value,
|
||||
_odictnode_HASH(node)) != 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
|
@ -1288,6 +1310,7 @@ odict_reversed(PyObject *op, PyObject *Py_UNUSED(ignored))
|
|||
/* move_to_end() */
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
OrderedDict.move_to_end
|
||||
|
||||
key: object
|
||||
|
|
@ -1300,7 +1323,7 @@ Raise KeyError if the element does not exist.
|
|||
|
||||
static PyObject *
|
||||
OrderedDict_move_to_end_impl(PyODictObject *self, PyObject *key, int last)
|
||||
/*[clinic end generated code: output=fafa4c5cc9b92f20 input=d6ceff7132a2fcd7]*/
|
||||
/*[clinic end generated code: output=fafa4c5cc9b92f20 input=09f8bc7053c0f6d4]*/
|
||||
{
|
||||
_ODictNode *node;
|
||||
|
||||
|
|
@ -1341,10 +1364,8 @@ static PyMethodDef odict_methods[] = {
|
|||
|
||||
/* overridden dict methods */
|
||||
ORDEREDDICT_FROMKEYS_METHODDEF
|
||||
{"__sizeof__", odict_sizeof, METH_NOARGS,
|
||||
odict_sizeof__doc__},
|
||||
{"__reduce__", odict_reduce, METH_NOARGS,
|
||||
odict_reduce__doc__},
|
||||
ORDEREDDICT___SIZEOF___METHODDEF
|
||||
ORDEREDDICT___REDUCE___METHODDEF
|
||||
ORDEREDDICT_SETDEFAULT_METHODDEF
|
||||
ORDEREDDICT_POP_METHODDEF
|
||||
ORDEREDDICT_POPITEM_METHODDEF
|
||||
|
|
@ -1356,11 +1377,8 @@ static PyMethodDef odict_methods[] = {
|
|||
odict_items__doc__},
|
||||
{"update", _PyCFunction_CAST(odict_update), METH_VARARGS | METH_KEYWORDS,
|
||||
odict_update__doc__},
|
||||
{"clear", odict_clear, METH_NOARGS,
|
||||
odict_clear__doc__},
|
||||
{"copy", odict_copy, METH_NOARGS,
|
||||
odict_copy__doc__},
|
||||
|
||||
ORDEREDDICT_CLEAR_METHODDEF
|
||||
ORDEREDDICT_COPY_METHODDEF
|
||||
/* new methods */
|
||||
{"__reversed__", odict_reversed, METH_NOARGS,
|
||||
odict_reversed__doc__},
|
||||
|
|
@ -1459,7 +1477,8 @@ odict_tp_clear(PyObject *op)
|
|||
{
|
||||
PyODictObject *od = _PyODictObject_CAST(op);
|
||||
Py_CLEAR(od->od_inst_dict);
|
||||
PyDict_Clear((PyObject *)od);
|
||||
// cannot use lock held variant as critical section is not held here
|
||||
PyDict_Clear(op);
|
||||
_odict_clear_nodes(od);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1467,7 +1486,7 @@ odict_tp_clear(PyObject *op)
|
|||
/* tp_richcompare */
|
||||
|
||||
static PyObject *
|
||||
odict_richcompare(PyObject *v, PyObject *w, int op)
|
||||
odict_richcompare_lock_held(PyObject *v, PyObject *w, int op)
|
||||
{
|
||||
if (!PyODict_Check(v) || !PyDict_Check(w)) {
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
|
|
@ -1500,6 +1519,16 @@ odict_richcompare(PyObject *v, PyObject *w, int op)
|
|||
}
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
odict_richcompare(PyObject *v, PyObject *w, int op)
|
||||
{
|
||||
PyObject *res;
|
||||
Py_BEGIN_CRITICAL_SECTION2(v, w);
|
||||
res = odict_richcompare_lock_held(v, w, op);
|
||||
Py_END_CRITICAL_SECTION2();
|
||||
return res;
|
||||
}
|
||||
|
||||
/* tp_iter */
|
||||
|
||||
static PyObject *
|
||||
|
|
@ -1590,10 +1619,11 @@ PyODict_New(void)
|
|||
}
|
||||
|
||||
static int
|
||||
_PyODict_SetItem_KnownHash(PyObject *od, PyObject *key, PyObject *value,
|
||||
Py_hash_t hash)
|
||||
_PyODict_SetItem_KnownHash_LockHeld(PyObject *od, PyObject *key, PyObject *value,
|
||||
Py_hash_t hash)
|
||||
{
|
||||
int res = _PyDict_SetItem_KnownHash(od, key, value, hash);
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
int res = _PyDict_SetItem_KnownHash_LockHeld((PyDictObject *)od, key, value, hash);
|
||||
if (res == 0) {
|
||||
res = _odict_add_new_node(_PyODictObject_CAST(od), key, hash);
|
||||
if (res < 0) {
|
||||
|
|
@ -1606,18 +1636,32 @@ _PyODict_SetItem_KnownHash(PyObject *od, PyObject *key, PyObject *value,
|
|||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
PyODict_SetItem(PyObject *od, PyObject *key, PyObject *value)
|
||||
|
||||
static int
|
||||
PyODict_SetItem_LockHeld(PyObject *od, PyObject *key, PyObject *value)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
Py_hash_t hash = PyObject_Hash(key);
|
||||
if (hash == -1)
|
||||
if (hash == -1) {
|
||||
return -1;
|
||||
return _PyODict_SetItem_KnownHash(od, key, value, hash);
|
||||
}
|
||||
return _PyODict_SetItem_KnownHash_LockHeld(od, key, value, hash);
|
||||
}
|
||||
|
||||
int
|
||||
PyODict_DelItem(PyObject *od, PyObject *key)
|
||||
PyODict_SetItem(PyObject *od, PyObject *key, PyObject *value)
|
||||
{
|
||||
int res;
|
||||
Py_BEGIN_CRITICAL_SECTION(od);
|
||||
res = PyODict_SetItem_LockHeld(od, key, value);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
PyODict_DelItem_LockHeld(PyObject *od, PyObject *key)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od);
|
||||
int res;
|
||||
Py_hash_t hash = PyObject_Hash(key);
|
||||
if (hash == -1)
|
||||
|
|
@ -1625,9 +1669,18 @@ PyODict_DelItem(PyObject *od, PyObject *key)
|
|||
res = _odict_clear_node(_PyODictObject_CAST(od), NULL, key, hash);
|
||||
if (res < 0)
|
||||
return -1;
|
||||
return _PyDict_DelItem_KnownHash(od, key, hash);
|
||||
return _PyDict_DelItem_KnownHash_LockHeld(od, key, hash);
|
||||
}
|
||||
|
||||
int
|
||||
PyODict_DelItem(PyObject *od, PyObject *key)
|
||||
{
|
||||
int res;
|
||||
Py_BEGIN_CRITICAL_SECTION(od);
|
||||
res = PyODict_DelItem_LockHeld(od, key);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
return res;
|
||||
}
|
||||
|
||||
/* -------------------------------------------
|
||||
* The OrderedDict views (keys/values/items)
|
||||
|
|
@ -1669,14 +1722,14 @@ odictiter_traverse(PyObject *op, visitproc visit, void *arg)
|
|||
/* In order to protect against modifications during iteration, we track
|
||||
* the current key instead of the current node. */
|
||||
static PyObject *
|
||||
odictiter_nextkey(odictiterobject *di)
|
||||
odictiter_nextkey_lock_held(odictiterobject *di)
|
||||
{
|
||||
assert(di->di_odict != NULL);
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(di->di_odict);
|
||||
PyObject *key = NULL;
|
||||
_ODictNode *node;
|
||||
int reversed = di->kind & _odict_ITER_REVERSED;
|
||||
|
||||
if (di->di_odict == NULL)
|
||||
return NULL;
|
||||
if (di->di_current == NULL)
|
||||
goto done; /* We're already done. */
|
||||
|
||||
|
|
@ -1721,8 +1774,23 @@ done:
|
|||
return key;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
odictiter_iternext(PyObject *op)
|
||||
odictiter_nextkey(odictiterobject *di)
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(di);
|
||||
if (di->di_odict == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *res;
|
||||
Py_BEGIN_CRITICAL_SECTION(di->di_odict);
|
||||
res = odictiter_nextkey_lock_held(di);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
return res;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
odictiter_iternext_lock_held(PyObject *op)
|
||||
{
|
||||
odictiterobject *di = (odictiterobject*)op;
|
||||
PyObject *result, *value;
|
||||
|
|
@ -1736,14 +1804,12 @@ odictiter_iternext(PyObject *op)
|
|||
return key;
|
||||
}
|
||||
|
||||
value = PyODict_GetItem((PyObject *)di->di_odict, key); /* borrowed */
|
||||
if (value == NULL) {
|
||||
if (PyDict_GetItemRef((PyObject *)di->di_odict, key, &value) != 1) {
|
||||
if (!PyErr_Occurred())
|
||||
PyErr_SetObject(PyExc_KeyError, key);
|
||||
Py_DECREF(key);
|
||||
goto done;
|
||||
}
|
||||
Py_INCREF(value);
|
||||
|
||||
/* Handle the values case. */
|
||||
if (!(di->kind & _odict_ITER_KEYS)) {
|
||||
|
|
@ -1754,7 +1820,7 @@ odictiter_iternext(PyObject *op)
|
|||
/* Handle the items case. */
|
||||
result = di->di_result;
|
||||
|
||||
if (Py_REFCNT(result) == 1) {
|
||||
if (_PyObject_IsUniquelyReferenced(result)) {
|
||||
/* not in use so we can reuse it
|
||||
* (the common case during iteration) */
|
||||
Py_INCREF(result);
|
||||
|
|
@ -1783,6 +1849,17 @@ done:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
odictiter_iternext(PyObject *op)
|
||||
{
|
||||
PyObject *res;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
res = odictiter_iternext_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* No need for tp_clear because odictiterobject is not mutable. */
|
||||
|
||||
PyDoc_STRVAR(reduce_doc, "Return state information for pickling");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue