mirror of
https://github.com/python/cpython.git
synced 2025-10-14 18:59:46 +00:00
gh-124296: Remove private dictionary version tag (PEP 699) (#124472)
This commit is contained in:
parent
60ff67d010
commit
5aa91c56bf
13 changed files with 35 additions and 354 deletions
|
@ -14,16 +14,11 @@ typedef struct {
|
||||||
/* Number of items in the dictionary */
|
/* Number of items in the dictionary */
|
||||||
Py_ssize_t ma_used;
|
Py_ssize_t ma_used;
|
||||||
|
|
||||||
/* Dictionary version: globally unique, value change each time
|
/* This is a private field for CPython's internal use.
|
||||||
the dictionary is modified */
|
* Bits 0-7 are for dict watchers.
|
||||||
#ifdef Py_BUILD_CORE
|
|
||||||
/* Bits 0-7 are for dict watchers.
|
|
||||||
* Bits 8-11 are for the watched mutation counter (used by tier2 optimization)
|
* Bits 8-11 are for the watched mutation counter (used by tier2 optimization)
|
||||||
* The remaining bits (12-63) are the actual version tag. */
|
* The remaining bits are not currently used. */
|
||||||
uint64_t ma_version_tag;
|
uint64_t _ma_watcher_tag;
|
||||||
#else
|
|
||||||
Py_DEPRECATED(3.12) uint64_t ma_version_tag;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PyDictKeysObject *ma_keys;
|
PyDictKeysObject *ma_keys;
|
||||||
|
|
||||||
|
|
|
@ -230,31 +230,6 @@ static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) {
|
||||||
#define DICT_WATCHER_MASK ((1 << DICT_MAX_WATCHERS) - 1)
|
#define DICT_WATCHER_MASK ((1 << DICT_MAX_WATCHERS) - 1)
|
||||||
#define DICT_WATCHER_AND_MODIFICATION_MASK ((1 << (DICT_MAX_WATCHERS + DICT_WATCHED_MUTATION_BITS)) - 1)
|
#define DICT_WATCHER_AND_MODIFICATION_MASK ((1 << (DICT_MAX_WATCHERS + DICT_WATCHED_MUTATION_BITS)) - 1)
|
||||||
|
|
||||||
#ifdef Py_GIL_DISABLED
|
|
||||||
|
|
||||||
#define THREAD_LOCAL_DICT_VERSION_COUNT 256
|
|
||||||
#define THREAD_LOCAL_DICT_VERSION_BATCH THREAD_LOCAL_DICT_VERSION_COUNT * DICT_VERSION_INCREMENT
|
|
||||||
|
|
||||||
static inline uint64_t
|
|
||||||
dict_next_version(PyInterpreterState *interp)
|
|
||||||
{
|
|
||||||
PyThreadState *tstate = PyThreadState_GET();
|
|
||||||
uint64_t cur_progress = (tstate->dict_global_version &
|
|
||||||
(THREAD_LOCAL_DICT_VERSION_BATCH - 1));
|
|
||||||
if (cur_progress == 0) {
|
|
||||||
uint64_t next = _Py_atomic_add_uint64(&interp->dict_state.global_version,
|
|
||||||
THREAD_LOCAL_DICT_VERSION_BATCH);
|
|
||||||
tstate->dict_global_version = next;
|
|
||||||
}
|
|
||||||
return tstate->dict_global_version += DICT_VERSION_INCREMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DICT_NEXT_VERSION(INTERP) dict_next_version(INTERP)
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define DICT_NEXT_VERSION(INTERP) \
|
|
||||||
((INTERP)->dict_state.global_version += DICT_VERSION_INCREMENT)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PyAPI_FUNC(void)
|
PyAPI_FUNC(void)
|
||||||
_PyDict_SendEvent(int watcher_bits,
|
_PyDict_SendEvent(int watcher_bits,
|
||||||
|
@ -263,7 +238,7 @@ _PyDict_SendEvent(int watcher_bits,
|
||||||
PyObject *key,
|
PyObject *key,
|
||||||
PyObject *value);
|
PyObject *value);
|
||||||
|
|
||||||
static inline uint64_t
|
static inline void
|
||||||
_PyDict_NotifyEvent(PyInterpreterState *interp,
|
_PyDict_NotifyEvent(PyInterpreterState *interp,
|
||||||
PyDict_WatchEvent event,
|
PyDict_WatchEvent event,
|
||||||
PyDictObject *mp,
|
PyDictObject *mp,
|
||||||
|
@ -271,12 +246,11 @@ _PyDict_NotifyEvent(PyInterpreterState *interp,
|
||||||
PyObject *value)
|
PyObject *value)
|
||||||
{
|
{
|
||||||
assert(Py_REFCNT((PyObject*)mp) > 0);
|
assert(Py_REFCNT((PyObject*)mp) > 0);
|
||||||
int watcher_bits = mp->ma_version_tag & DICT_WATCHER_MASK;
|
int watcher_bits = mp->_ma_watcher_tag & DICT_WATCHER_MASK;
|
||||||
if (watcher_bits) {
|
if (watcher_bits) {
|
||||||
RARE_EVENT_STAT_INC(watched_dict_modification);
|
RARE_EVENT_STAT_INC(watched_dict_modification);
|
||||||
_PyDict_SendEvent(watcher_bits, event, mp, key, value);
|
_PyDict_SendEvent(watcher_bits, event, mp, key, value);
|
||||||
}
|
}
|
||||||
return DICT_NEXT_VERSION(interp) | (mp->ma_version_tag & DICT_WATCHER_AND_MODIFICATION_MASK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern PyDictObject *_PyObject_MaterializeManagedDict(PyObject *obj);
|
extern PyDictObject *_PyObject_MaterializeManagedDict(PyObject *obj);
|
||||||
|
|
|
@ -12,10 +12,6 @@ extern "C" {
|
||||||
#define DICT_WATCHED_MUTATION_BITS 4
|
#define DICT_WATCHED_MUTATION_BITS 4
|
||||||
|
|
||||||
struct _Py_dict_state {
|
struct _Py_dict_state {
|
||||||
/*Global counter used to set ma_version_tag field of dictionary.
|
|
||||||
* It is incremented each time that a dictionary is created and each
|
|
||||||
* time that a dictionary is modified. */
|
|
||||||
uint64_t global_version;
|
|
||||||
uint32_t next_keys_version;
|
uint32_t next_keys_version;
|
||||||
PyDict_WatchCallback watchers[DICT_MAX_WATCHERS];
|
PyDict_WatchCallback watchers[DICT_MAX_WATCHERS];
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,191 +0,0 @@
|
||||||
"""
|
|
||||||
Test implementation of the PEP 509: dictionary versioning.
|
|
||||||
"""
|
|
||||||
import unittest
|
|
||||||
from test.support import import_helper
|
|
||||||
|
|
||||||
# PEP 509 is implemented in CPython but other Python implementations
|
|
||||||
# don't require to implement it
|
|
||||||
_testcapi = import_helper.import_module('_testcapi')
|
|
||||||
|
|
||||||
|
|
||||||
class DictVersionTests(unittest.TestCase):
|
|
||||||
type2test = dict
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.seen_versions = set()
|
|
||||||
self.dict = None
|
|
||||||
|
|
||||||
def check_version_unique(self, mydict):
|
|
||||||
version = _testcapi.dict_get_version(mydict)
|
|
||||||
self.assertNotIn(version, self.seen_versions)
|
|
||||||
self.seen_versions.add(version)
|
|
||||||
|
|
||||||
def check_version_changed(self, mydict, method, *args, **kw):
|
|
||||||
result = method(*args, **kw)
|
|
||||||
self.check_version_unique(mydict)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def check_version_dont_change(self, mydict, method, *args, **kw):
|
|
||||||
version1 = _testcapi.dict_get_version(mydict)
|
|
||||||
self.seen_versions.add(version1)
|
|
||||||
|
|
||||||
result = method(*args, **kw)
|
|
||||||
|
|
||||||
version2 = _testcapi.dict_get_version(mydict)
|
|
||||||
self.assertEqual(version2, version1, "version changed")
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def new_dict(self, *args, **kw):
|
|
||||||
d = self.type2test(*args, **kw)
|
|
||||||
self.check_version_unique(d)
|
|
||||||
return d
|
|
||||||
|
|
||||||
def test_constructor(self):
|
|
||||||
# new empty dictionaries must all have an unique version
|
|
||||||
empty1 = self.new_dict()
|
|
||||||
empty2 = self.new_dict()
|
|
||||||
empty3 = self.new_dict()
|
|
||||||
|
|
||||||
# non-empty dictionaries must also have an unique version
|
|
||||||
nonempty1 = self.new_dict(x='x')
|
|
||||||
nonempty2 = self.new_dict(x='x', y='y')
|
|
||||||
|
|
||||||
def test_copy(self):
|
|
||||||
d = self.new_dict(a=1, b=2)
|
|
||||||
|
|
||||||
d2 = self.check_version_dont_change(d, d.copy)
|
|
||||||
|
|
||||||
# dict.copy() must create a dictionary with a new unique version
|
|
||||||
self.check_version_unique(d2)
|
|
||||||
|
|
||||||
def test_setitem(self):
|
|
||||||
d = self.new_dict()
|
|
||||||
|
|
||||||
# creating new keys must change the version
|
|
||||||
self.check_version_changed(d, d.__setitem__, 'x', 'x')
|
|
||||||
self.check_version_changed(d, d.__setitem__, 'y', 'y')
|
|
||||||
|
|
||||||
# changing values must change the version
|
|
||||||
self.check_version_changed(d, d.__setitem__, 'x', 1)
|
|
||||||
self.check_version_changed(d, d.__setitem__, 'y', 2)
|
|
||||||
|
|
||||||
def test_setitem_same_value(self):
|
|
||||||
value = object()
|
|
||||||
d = self.new_dict()
|
|
||||||
|
|
||||||
# setting a key must change the version
|
|
||||||
self.check_version_changed(d, d.__setitem__, 'key', value)
|
|
||||||
|
|
||||||
# setting a key to the same value with dict.__setitem__
|
|
||||||
# must change the version
|
|
||||||
self.check_version_dont_change(d, d.__setitem__, 'key', value)
|
|
||||||
|
|
||||||
# setting a key to the same value with dict.update
|
|
||||||
# must change the version
|
|
||||||
self.check_version_dont_change(d, d.update, key=value)
|
|
||||||
|
|
||||||
d2 = self.new_dict(key=value)
|
|
||||||
self.check_version_dont_change(d, d.update, d2)
|
|
||||||
|
|
||||||
def test_setitem_equal(self):
|
|
||||||
class AlwaysEqual:
|
|
||||||
def __eq__(self, other):
|
|
||||||
return True
|
|
||||||
|
|
||||||
value1 = AlwaysEqual()
|
|
||||||
value2 = AlwaysEqual()
|
|
||||||
self.assertTrue(value1 == value2)
|
|
||||||
self.assertFalse(value1 != value2)
|
|
||||||
self.assertIsNot(value1, value2)
|
|
||||||
|
|
||||||
d = self.new_dict()
|
|
||||||
self.check_version_changed(d, d.__setitem__, 'key', value1)
|
|
||||||
self.assertIs(d['key'], value1)
|
|
||||||
|
|
||||||
# setting a key to a value equal to the current value
|
|
||||||
# with dict.__setitem__() must change the version
|
|
||||||
self.check_version_changed(d, d.__setitem__, 'key', value2)
|
|
||||||
self.assertIs(d['key'], value2)
|
|
||||||
|
|
||||||
# setting a key to a value equal to the current value
|
|
||||||
# with dict.update() must change the version
|
|
||||||
self.check_version_changed(d, d.update, key=value1)
|
|
||||||
self.assertIs(d['key'], value1)
|
|
||||||
|
|
||||||
d2 = self.new_dict(key=value2)
|
|
||||||
self.check_version_changed(d, d.update, d2)
|
|
||||||
self.assertIs(d['key'], value2)
|
|
||||||
|
|
||||||
def test_setdefault(self):
|
|
||||||
d = self.new_dict()
|
|
||||||
|
|
||||||
# setting a key with dict.setdefault() must change the version
|
|
||||||
self.check_version_changed(d, d.setdefault, 'key', 'value1')
|
|
||||||
|
|
||||||
# don't change the version if the key already exists
|
|
||||||
self.check_version_dont_change(d, d.setdefault, 'key', 'value2')
|
|
||||||
|
|
||||||
def test_delitem(self):
|
|
||||||
d = self.new_dict(key='value')
|
|
||||||
|
|
||||||
# deleting a key with dict.__delitem__() must change the version
|
|
||||||
self.check_version_changed(d, d.__delitem__, 'key')
|
|
||||||
|
|
||||||
# don't change the version if the key doesn't exist
|
|
||||||
self.check_version_dont_change(d, self.assertRaises, KeyError,
|
|
||||||
d.__delitem__, 'key')
|
|
||||||
|
|
||||||
def test_pop(self):
|
|
||||||
d = self.new_dict(key='value')
|
|
||||||
|
|
||||||
# pop() must change the version if the key exists
|
|
||||||
self.check_version_changed(d, d.pop, 'key')
|
|
||||||
|
|
||||||
# pop() must not change the version if the key does not exist
|
|
||||||
self.check_version_dont_change(d, self.assertRaises, KeyError,
|
|
||||||
d.pop, 'key')
|
|
||||||
|
|
||||||
def test_popitem(self):
|
|
||||||
d = self.new_dict(key='value')
|
|
||||||
|
|
||||||
# popitem() must change the version if the dict is not empty
|
|
||||||
self.check_version_changed(d, d.popitem)
|
|
||||||
|
|
||||||
# popitem() must not change the version if the dict is empty
|
|
||||||
self.check_version_dont_change(d, self.assertRaises, KeyError,
|
|
||||||
d.popitem)
|
|
||||||
|
|
||||||
def test_update(self):
|
|
||||||
d = self.new_dict(key='value')
|
|
||||||
|
|
||||||
# update() calling with no argument must not change the version
|
|
||||||
self.check_version_dont_change(d, d.update)
|
|
||||||
|
|
||||||
# update() must change the version
|
|
||||||
self.check_version_changed(d, d.update, key='new value')
|
|
||||||
|
|
||||||
d2 = self.new_dict(key='value 3')
|
|
||||||
self.check_version_changed(d, d.update, d2)
|
|
||||||
|
|
||||||
def test_clear(self):
|
|
||||||
d = self.new_dict(key='value')
|
|
||||||
|
|
||||||
# clear() must change the version if the dict is not empty
|
|
||||||
self.check_version_changed(d, d.clear)
|
|
||||||
|
|
||||||
# clear() must not change the version if the dict is empty
|
|
||||||
self.check_version_dont_change(d, d.clear)
|
|
||||||
|
|
||||||
|
|
||||||
class Dict(dict):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class DictSubtypeVersionTests(DictVersionTests):
|
|
||||||
type2test = Dict
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
|
@ -142,41 +142,6 @@ class TestDict(TestCase):
|
||||||
for ref in thread_list:
|
for ref in thread_list:
|
||||||
self.assertIsNone(ref())
|
self.assertIsNone(ref())
|
||||||
|
|
||||||
@unittest.skipIf(_testcapi is None, 'need _testcapi module')
|
|
||||||
def test_dict_version(self):
|
|
||||||
dict_version = _testcapi.dict_version
|
|
||||||
THREAD_COUNT = 10
|
|
||||||
DICT_COUNT = 10000
|
|
||||||
lists = []
|
|
||||||
writers = []
|
|
||||||
|
|
||||||
def writer_func(thread_list):
|
|
||||||
for i in range(DICT_COUNT):
|
|
||||||
thread_list.append(dict_version({}))
|
|
||||||
|
|
||||||
for x in range(THREAD_COUNT):
|
|
||||||
thread_list = []
|
|
||||||
lists.append(thread_list)
|
|
||||||
writer = Thread(target=partial(writer_func, thread_list))
|
|
||||||
writers.append(writer)
|
|
||||||
|
|
||||||
for writer in writers:
|
|
||||||
writer.start()
|
|
||||||
|
|
||||||
for writer in writers:
|
|
||||||
writer.join()
|
|
||||||
|
|
||||||
total_len = 0
|
|
||||||
values = set()
|
|
||||||
for thread_list in lists:
|
|
||||||
for v in thread_list:
|
|
||||||
if v in values:
|
|
||||||
print('dup', v, (v/4096)%256)
|
|
||||||
values.add(v)
|
|
||||||
total_len += len(thread_list)
|
|
||||||
versions = set(dict_version for thread_list in lists for dict_version in thread_list)
|
|
||||||
self.assertEqual(len(versions), THREAD_COUNT*DICT_COUNT)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
:c:type:`PyDictObject` no longer maintains a private version tag field
|
||||||
|
``ma_version_tag`` per :pep:`699`. This field was originally added in
|
||||||
|
Python 3.6 (:pep:`509`) and deprecated in Python 3.12.
|
|
@ -181,19 +181,6 @@ dict_popstring_null(PyObject *self, PyObject *args)
|
||||||
RETURN_INT(PyDict_PopString(dict, key, NULL));
|
RETURN_INT(PyDict_PopString(dict, key, NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
dict_version(PyObject *self, PyObject *dict)
|
|
||||||
{
|
|
||||||
if (!PyDict_Check(dict)) {
|
|
||||||
PyErr_SetString(PyExc_TypeError, "expected dict");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
_Py_COMP_DIAG_PUSH
|
|
||||||
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
|
|
||||||
return PyLong_FromUnsignedLongLong(((PyDictObject *)dict)->ma_version_tag);
|
|
||||||
_Py_COMP_DIAG_POP
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyMethodDef test_methods[] = {
|
static PyMethodDef test_methods[] = {
|
||||||
{"dict_containsstring", dict_containsstring, METH_VARARGS},
|
{"dict_containsstring", dict_containsstring, METH_VARARGS},
|
||||||
{"dict_getitemref", dict_getitemref, METH_VARARGS},
|
{"dict_getitemref", dict_getitemref, METH_VARARGS},
|
||||||
|
@ -204,7 +191,6 @@ static PyMethodDef test_methods[] = {
|
||||||
{"dict_pop_null", dict_pop_null, METH_VARARGS},
|
{"dict_pop_null", dict_pop_null, METH_VARARGS},
|
||||||
{"dict_popstring", dict_popstring, METH_VARARGS},
|
{"dict_popstring", dict_popstring, METH_VARARGS},
|
||||||
{"dict_popstring_null", dict_popstring_null, METH_VARARGS},
|
{"dict_popstring_null", dict_popstring_null, METH_VARARGS},
|
||||||
{"dict_version", dict_version, METH_O},
|
|
||||||
{NULL},
|
{NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1909,25 +1909,6 @@ getitem_with_error(PyObject *self, PyObject *args)
|
||||||
return PyObject_GetItem(map, key);
|
return PyObject_GetItem(map, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
dict_get_version(PyObject *self, PyObject *args)
|
|
||||||
{
|
|
||||||
PyDictObject *dict;
|
|
||||||
uint64_t version;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, &dict))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
_Py_COMP_DIAG_PUSH
|
|
||||||
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
|
|
||||||
version = dict->ma_version_tag;
|
|
||||||
_Py_COMP_DIAG_POP
|
|
||||||
|
|
||||||
static_assert(sizeof(unsigned long long) >= sizeof(version),
|
|
||||||
"version is larger than unsigned long long");
|
|
||||||
return PyLong_FromUnsignedLongLong((unsigned long long)version);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
raise_SIGINT_then_send_None(PyObject *self, PyObject *args)
|
raise_SIGINT_then_send_None(PyObject *self, PyObject *args)
|
||||||
|
@ -3407,7 +3388,6 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"return_result_with_error", return_result_with_error, METH_NOARGS},
|
{"return_result_with_error", return_result_with_error, METH_NOARGS},
|
||||||
{"getitem_with_error", getitem_with_error, METH_VARARGS},
|
{"getitem_with_error", getitem_with_error, METH_VARARGS},
|
||||||
{"Py_CompileString", pycompilestring, METH_O},
|
{"Py_CompileString", pycompilestring, METH_O},
|
||||||
{"dict_get_version", dict_get_version, METH_VARARGS},
|
|
||||||
{"raise_SIGINT_then_send_None", raise_SIGINT_then_send_None, METH_VARARGS},
|
{"raise_SIGINT_then_send_None", raise_SIGINT_then_send_None, METH_VARARGS},
|
||||||
{"stack_pointer", stack_pointer, METH_NOARGS},
|
{"stack_pointer", stack_pointer, METH_NOARGS},
|
||||||
#ifdef W_STOPCODE
|
#ifdef W_STOPCODE
|
||||||
|
|
|
@ -877,7 +877,7 @@ new_dict(PyInterpreterState *interp,
|
||||||
mp->ma_keys = keys;
|
mp->ma_keys = keys;
|
||||||
mp->ma_values = values;
|
mp->ma_values = values;
|
||||||
mp->ma_used = used;
|
mp->ma_used = used;
|
||||||
mp->ma_version_tag = DICT_NEXT_VERSION(interp);
|
mp->_ma_watcher_tag = 0;
|
||||||
ASSERT_CONSISTENT(mp);
|
ASSERT_CONSISTENT(mp);
|
||||||
return (PyObject *)mp;
|
return (PyObject *)mp;
|
||||||
}
|
}
|
||||||
|
@ -1678,8 +1678,7 @@ insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value);
|
||||||
interp, PyDict_EVENT_ADDED, mp, key, value);
|
|
||||||
mp->ma_keys->dk_version = 0;
|
mp->ma_keys->dk_version = 0;
|
||||||
|
|
||||||
Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash);
|
Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash);
|
||||||
|
@ -1698,7 +1697,6 @@ insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp,
|
||||||
STORE_VALUE(ep, value);
|
STORE_VALUE(ep, value);
|
||||||
STORE_HASH(ep, hash);
|
STORE_HASH(ep, hash);
|
||||||
}
|
}
|
||||||
mp->ma_version_tag = new_version;
|
|
||||||
STORE_KEYS_USABLE(mp->ma_keys, mp->ma_keys->dk_usable - 1);
|
STORE_KEYS_USABLE(mp->ma_keys, mp->ma_keys->dk_usable - 1);
|
||||||
STORE_KEYS_NENTRIES(mp->ma_keys, mp->ma_keys->dk_nentries + 1);
|
STORE_KEYS_NENTRIES(mp->ma_keys, mp->ma_keys->dk_nentries + 1);
|
||||||
assert(mp->ma_keys->dk_usable >= 0);
|
assert(mp->ma_keys->dk_usable >= 0);
|
||||||
|
@ -1744,16 +1742,14 @@ insert_split_value(PyInterpreterState *interp, PyDictObject *mp, PyObject *key,
|
||||||
MAINTAIN_TRACKING(mp, key, value);
|
MAINTAIN_TRACKING(mp, key, value);
|
||||||
PyObject *old_value = mp->ma_values->values[ix];
|
PyObject *old_value = mp->ma_values->values[ix];
|
||||||
if (old_value == NULL) {
|
if (old_value == NULL) {
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value);
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value);
|
||||||
STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value));
|
STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value));
|
||||||
_PyDictValues_AddToInsertionOrder(mp->ma_values, ix);
|
_PyDictValues_AddToInsertionOrder(mp->ma_values, ix);
|
||||||
STORE_USED(mp, mp->ma_used + 1);
|
STORE_USED(mp, mp->ma_used + 1);
|
||||||
mp->ma_version_tag = new_version;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value);
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value);
|
||||||
STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value));
|
STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value));
|
||||||
mp->ma_version_tag = new_version;
|
|
||||||
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
|
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
|
||||||
// when dict only holds the strong reference to value in ep->me_value.
|
// when dict only holds the strong reference to value in ep->me_value.
|
||||||
Py_DECREF(old_value);
|
Py_DECREF(old_value);
|
||||||
|
@ -1815,8 +1811,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (old_value != value) {
|
if (old_value != value) {
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value);
|
||||||
interp, PyDict_EVENT_MODIFIED, mp, key, value);
|
|
||||||
assert(old_value != NULL);
|
assert(old_value != NULL);
|
||||||
assert(!_PyDict_HasSplitTable(mp));
|
assert(!_PyDict_HasSplitTable(mp));
|
||||||
if (DK_IS_UNICODE(mp->ma_keys)) {
|
if (DK_IS_UNICODE(mp->ma_keys)) {
|
||||||
|
@ -1827,7 +1822,6 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
|
||||||
PyDictKeyEntry *ep = &DK_ENTRIES(mp->ma_keys)[ix];
|
PyDictKeyEntry *ep = &DK_ENTRIES(mp->ma_keys)[ix];
|
||||||
STORE_VALUE(ep, value);
|
STORE_VALUE(ep, value);
|
||||||
}
|
}
|
||||||
mp->ma_version_tag = new_version;
|
|
||||||
}
|
}
|
||||||
Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */
|
Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */
|
||||||
ASSERT_CONSISTENT(mp);
|
ASSERT_CONSISTENT(mp);
|
||||||
|
@ -1857,8 +1851,7 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
|
||||||
Py_DECREF(value);
|
Py_DECREF(value);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value);
|
||||||
interp, PyDict_EVENT_ADDED, mp, key, value);
|
|
||||||
|
|
||||||
/* We don't decref Py_EMPTY_KEYS here because it is immortal. */
|
/* We don't decref Py_EMPTY_KEYS here because it is immortal. */
|
||||||
assert(mp->ma_values == NULL);
|
assert(mp->ma_values == NULL);
|
||||||
|
@ -1879,7 +1872,6 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
|
||||||
STORE_VALUE(ep, value);
|
STORE_VALUE(ep, value);
|
||||||
}
|
}
|
||||||
STORE_USED(mp, mp->ma_used + 1);
|
STORE_USED(mp, mp->ma_used + 1);
|
||||||
mp->ma_version_tag = new_version;
|
|
||||||
newkeys->dk_usable--;
|
newkeys->dk_usable--;
|
||||||
newkeys->dk_nentries++;
|
newkeys->dk_nentries++;
|
||||||
// We store the keys last so no one can see them in a partially inconsistent
|
// We store the keys last so no one can see them in a partially inconsistent
|
||||||
|
@ -2612,7 +2604,7 @@ delete_index_from_values(PyDictValues *values, Py_ssize_t ix)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
|
delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
|
||||||
PyObject *old_value, uint64_t new_version)
|
PyObject *old_value)
|
||||||
{
|
{
|
||||||
PyObject *old_key;
|
PyObject *old_key;
|
||||||
|
|
||||||
|
@ -2622,7 +2614,6 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
|
||||||
assert(hashpos >= 0);
|
assert(hashpos >= 0);
|
||||||
|
|
||||||
STORE_USED(mp, mp->ma_used - 1);
|
STORE_USED(mp, mp->ma_used - 1);
|
||||||
mp->ma_version_tag = new_version;
|
|
||||||
if (_PyDict_HasSplitTable(mp)) {
|
if (_PyDict_HasSplitTable(mp)) {
|
||||||
assert(old_value == mp->ma_values->values[ix]);
|
assert(old_value == mp->ma_values->values[ix]);
|
||||||
STORE_SPLIT_VALUE(mp, ix, NULL);
|
STORE_SPLIT_VALUE(mp, ix, NULL);
|
||||||
|
@ -2692,9 +2683,8 @@ delitem_knownhash_lock_held(PyObject *op, PyObject *key, Py_hash_t hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
||||||
interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
delitem_common(mp, hash, ix, old_value);
|
||||||
delitem_common(mp, hash, ix, old_value, new_version);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2740,9 +2730,8 @@ delitemif_lock_held(PyObject *op, PyObject *key,
|
||||||
|
|
||||||
if (res > 0) {
|
if (res > 0) {
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
||||||
interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
delitem_common(mp, hash, ix, old_value);
|
||||||
delitem_common(mp, hash, ix, old_value, new_version);
|
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2786,11 +2775,9 @@ clear_lock_held(PyObject *op)
|
||||||
}
|
}
|
||||||
/* Empty the dict... */
|
/* Empty the dict... */
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_CLEARED, mp, NULL, NULL);
|
||||||
interp, PyDict_EVENT_CLEARED, mp, NULL, NULL);
|
|
||||||
// We don't inc ref empty keys because they're immortal
|
// We don't inc ref empty keys because they're immortal
|
||||||
ensure_shared_on_resize(mp);
|
ensure_shared_on_resize(mp);
|
||||||
mp->ma_version_tag = new_version;
|
|
||||||
STORE_USED(mp, 0);
|
STORE_USED(mp, 0);
|
||||||
if (oldvalues == NULL) {
|
if (oldvalues == NULL) {
|
||||||
set_keys(mp, Py_EMPTY_KEYS);
|
set_keys(mp, Py_EMPTY_KEYS);
|
||||||
|
@ -2950,9 +2937,8 @@ _PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash,
|
||||||
|
|
||||||
assert(old_value != NULL);
|
assert(old_value != NULL);
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
||||||
interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
delitem_common(mp, hash, ix, Py_NewRef(old_value));
|
||||||
delitem_common(mp, hash, ix, Py_NewRef(old_value), new_version);
|
|
||||||
|
|
||||||
ASSERT_CONSISTENT(mp);
|
ASSERT_CONSISTENT(mp);
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -3717,8 +3703,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
|
||||||
(DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE ||
|
(DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE ||
|
||||||
USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used)
|
USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used)
|
||||||
) {
|
) {
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_CLONED, mp, (PyObject *)other, NULL);
|
||||||
interp, PyDict_EVENT_CLONED, mp, (PyObject *)other, NULL);
|
|
||||||
PyDictKeysObject *keys = clone_combined_dict_keys(other);
|
PyDictKeysObject *keys = clone_combined_dict_keys(other);
|
||||||
if (keys == NULL)
|
if (keys == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -3727,7 +3712,6 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
|
||||||
dictkeys_decref(interp, mp->ma_keys, IS_DICT_SHARED(mp));
|
dictkeys_decref(interp, mp->ma_keys, IS_DICT_SHARED(mp));
|
||||||
mp->ma_keys = keys;
|
mp->ma_keys = keys;
|
||||||
STORE_USED(mp, other->ma_used);
|
STORE_USED(mp, other->ma_used);
|
||||||
mp->ma_version_tag = new_version;
|
|
||||||
ASSERT_CONSISTENT(mp);
|
ASSERT_CONSISTENT(mp);
|
||||||
|
|
||||||
if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) {
|
if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) {
|
||||||
|
@ -3982,7 +3966,7 @@ copy_lock_held(PyObject *o)
|
||||||
split_copy->ma_values = newvalues;
|
split_copy->ma_values = newvalues;
|
||||||
split_copy->ma_keys = mp->ma_keys;
|
split_copy->ma_keys = mp->ma_keys;
|
||||||
split_copy->ma_used = mp->ma_used;
|
split_copy->ma_used = mp->ma_used;
|
||||||
split_copy->ma_version_tag = DICT_NEXT_VERSION(interp);
|
split_copy->_ma_watcher_tag = 0;
|
||||||
dictkeys_incref(mp->ma_keys);
|
dictkeys_incref(mp->ma_keys);
|
||||||
if (_PyObject_GC_IS_TRACKED(mp))
|
if (_PyObject_GC_IS_TRACKED(mp))
|
||||||
_PyObject_GC_TRACK(split_copy);
|
_PyObject_GC_TRACK(split_copy);
|
||||||
|
@ -4430,7 +4414,6 @@ dict_popitem_impl(PyDictObject *self)
|
||||||
{
|
{
|
||||||
Py_ssize_t i, j;
|
Py_ssize_t i, j;
|
||||||
PyObject *res;
|
PyObject *res;
|
||||||
uint64_t new_version;
|
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
|
||||||
ASSERT_DICT_LOCKED(self);
|
ASSERT_DICT_LOCKED(self);
|
||||||
|
@ -4473,8 +4456,7 @@ dict_popitem_impl(PyDictObject *self)
|
||||||
assert(i >= 0);
|
assert(i >= 0);
|
||||||
|
|
||||||
key = ep0[i].me_key;
|
key = ep0[i].me_key;
|
||||||
new_version = _PyDict_NotifyEvent(
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, self, key, NULL);
|
||||||
interp, PyDict_EVENT_DELETED, self, key, NULL);
|
|
||||||
hash = unicode_get_hash(key);
|
hash = unicode_get_hash(key);
|
||||||
value = ep0[i].me_value;
|
value = ep0[i].me_value;
|
||||||
ep0[i].me_key = NULL;
|
ep0[i].me_key = NULL;
|
||||||
|
@ -4489,8 +4471,7 @@ dict_popitem_impl(PyDictObject *self)
|
||||||
assert(i >= 0);
|
assert(i >= 0);
|
||||||
|
|
||||||
key = ep0[i].me_key;
|
key = ep0[i].me_key;
|
||||||
new_version = _PyDict_NotifyEvent(
|
_PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, self, key, NULL);
|
||||||
interp, PyDict_EVENT_DELETED, self, key, NULL);
|
|
||||||
hash = ep0[i].me_hash;
|
hash = ep0[i].me_hash;
|
||||||
value = ep0[i].me_value;
|
value = ep0[i].me_value;
|
||||||
ep0[i].me_key = NULL;
|
ep0[i].me_key = NULL;
|
||||||
|
@ -4508,7 +4489,6 @@ dict_popitem_impl(PyDictObject *self)
|
||||||
/* We can't dk_usable++ since there is DKIX_DUMMY in indices */
|
/* We can't dk_usable++ since there is DKIX_DUMMY in indices */
|
||||||
STORE_KEYS_NENTRIES(self->ma_keys, i);
|
STORE_KEYS_NENTRIES(self->ma_keys, i);
|
||||||
STORE_USED(self, self->ma_used - 1);
|
STORE_USED(self, self->ma_used - 1);
|
||||||
self->ma_version_tag = new_version;
|
|
||||||
ASSERT_CONSISTENT(self);
|
ASSERT_CONSISTENT(self);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -4759,8 +4739,7 @@ dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
PyDictObject *d = (PyDictObject *)self;
|
PyDictObject *d = (PyDictObject *)self;
|
||||||
|
|
||||||
d->ma_used = 0;
|
d->ma_used = 0;
|
||||||
d->ma_version_tag = DICT_NEXT_VERSION(
|
d->_ma_watcher_tag = 0;
|
||||||
_PyInterpreterState_GET());
|
|
||||||
dictkeys_incref(Py_EMPTY_KEYS);
|
dictkeys_incref(Py_EMPTY_KEYS);
|
||||||
d->ma_keys = Py_EMPTY_KEYS;
|
d->ma_keys = Py_EMPTY_KEYS;
|
||||||
d->ma_values = NULL;
|
d->ma_values = NULL;
|
||||||
|
@ -7384,7 +7363,7 @@ PyDict_Watch(int watcher_id, PyObject* dict)
|
||||||
if (validate_watcher_id(interp, watcher_id)) {
|
if (validate_watcher_id(interp, watcher_id)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
((PyDictObject*)dict)->ma_version_tag |= (1LL << watcher_id);
|
((PyDictObject*)dict)->_ma_watcher_tag |= (1LL << watcher_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7399,7 +7378,7 @@ PyDict_Unwatch(int watcher_id, PyObject* dict)
|
||||||
if (validate_watcher_id(interp, watcher_id)) {
|
if (validate_watcher_id(interp, watcher_id)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
((PyDictObject*)dict)->ma_version_tag &= ~(1LL << watcher_id);
|
((PyDictObject*)dict)->_ma_watcher_tag &= ~(1LL << watcher_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2254,7 +2254,6 @@ dummy_func(
|
||||||
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
|
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
|
||||||
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries);
|
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries);
|
||||||
PyObject *old_value;
|
PyObject *old_value;
|
||||||
uint64_t new_version;
|
|
||||||
DEOPT_IF(!DK_IS_UNICODE(dict->ma_keys));
|
DEOPT_IF(!DK_IS_UNICODE(dict->ma_keys));
|
||||||
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
|
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
|
||||||
DEOPT_IF(ep->me_key != name);
|
DEOPT_IF(ep->me_key != name);
|
||||||
|
@ -2264,9 +2263,8 @@ dummy_func(
|
||||||
}
|
}
|
||||||
old_value = ep->me_value;
|
old_value = ep->me_value;
|
||||||
PyDict_WatchEvent event = old_value == NULL ? PyDict_EVENT_ADDED : PyDict_EVENT_MODIFIED;
|
PyDict_WatchEvent event = old_value == NULL ? PyDict_EVENT_ADDED : PyDict_EVENT_MODIFIED;
|
||||||
new_version = _PyDict_NotifyEvent(tstate->interp, event, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
_PyDict_NotifyEvent(tstate->interp, event, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
||||||
ep->me_value = PyStackRef_AsPyObjectSteal(value);
|
ep->me_value = PyStackRef_AsPyObjectSteal(value);
|
||||||
dict->ma_version_tag = new_version; // PEP 509
|
|
||||||
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
|
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
|
||||||
// when dict only holds the strong reference to value in ep->me_value.
|
// when dict only holds the strong reference to value in ep->me_value.
|
||||||
Py_XDECREF(old_value);
|
Py_XDECREF(old_value);
|
||||||
|
|
4
Python/executor_cases.c.h
generated
4
Python/executor_cases.c.h
generated
|
@ -2625,7 +2625,6 @@
|
||||||
JUMP_TO_JUMP_TARGET();
|
JUMP_TO_JUMP_TARGET();
|
||||||
}
|
}
|
||||||
PyObject *old_value;
|
PyObject *old_value;
|
||||||
uint64_t new_version;
|
|
||||||
if (!DK_IS_UNICODE(dict->ma_keys)) {
|
if (!DK_IS_UNICODE(dict->ma_keys)) {
|
||||||
UOP_STAT_INC(uopcode, miss);
|
UOP_STAT_INC(uopcode, miss);
|
||||||
JUMP_TO_JUMP_TARGET();
|
JUMP_TO_JUMP_TARGET();
|
||||||
|
@ -2641,9 +2640,8 @@
|
||||||
}
|
}
|
||||||
old_value = ep->me_value;
|
old_value = ep->me_value;
|
||||||
PyDict_WatchEvent event = old_value == NULL ? PyDict_EVENT_ADDED : PyDict_EVENT_MODIFIED;
|
PyDict_WatchEvent event = old_value == NULL ? PyDict_EVENT_ADDED : PyDict_EVENT_MODIFIED;
|
||||||
new_version = _PyDict_NotifyEvent(tstate->interp, event, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
_PyDict_NotifyEvent(tstate->interp, event, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
||||||
ep->me_value = PyStackRef_AsPyObjectSteal(value);
|
ep->me_value = PyStackRef_AsPyObjectSteal(value);
|
||||||
dict->ma_version_tag = new_version; // PEP 509
|
|
||||||
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
|
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
|
||||||
// when dict only holds the strong reference to value in ep->me_value.
|
// when dict only holds the strong reference to value in ep->me_value.
|
||||||
Py_XDECREF(old_value);
|
Py_XDECREF(old_value);
|
||||||
|
|
4
Python/generated_cases.c.h
generated
4
Python/generated_cases.c.h
generated
|
@ -6940,7 +6940,6 @@
|
||||||
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
|
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
|
||||||
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR);
|
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR);
|
||||||
PyObject *old_value;
|
PyObject *old_value;
|
||||||
uint64_t new_version;
|
|
||||||
DEOPT_IF(!DK_IS_UNICODE(dict->ma_keys), STORE_ATTR);
|
DEOPT_IF(!DK_IS_UNICODE(dict->ma_keys), STORE_ATTR);
|
||||||
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
|
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
|
||||||
DEOPT_IF(ep->me_key != name, STORE_ATTR);
|
DEOPT_IF(ep->me_key != name, STORE_ATTR);
|
||||||
|
@ -6950,9 +6949,8 @@
|
||||||
}
|
}
|
||||||
old_value = ep->me_value;
|
old_value = ep->me_value;
|
||||||
PyDict_WatchEvent event = old_value == NULL ? PyDict_EVENT_ADDED : PyDict_EVENT_MODIFIED;
|
PyDict_WatchEvent event = old_value == NULL ? PyDict_EVENT_ADDED : PyDict_EVENT_MODIFIED;
|
||||||
new_version = _PyDict_NotifyEvent(tstate->interp, event, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
_PyDict_NotifyEvent(tstate->interp, event, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
||||||
ep->me_value = PyStackRef_AsPyObjectSteal(value);
|
ep->me_value = PyStackRef_AsPyObjectSteal(value);
|
||||||
dict->ma_version_tag = new_version; // PEP 509
|
|
||||||
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
|
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
|
||||||
// when dict only holds the strong reference to value in ep->me_value.
|
// when dict only holds the strong reference to value in ep->me_value.
|
||||||
Py_XDECREF(old_value);
|
Py_XDECREF(old_value);
|
||||||
|
|
|
@ -56,14 +56,14 @@ static int
|
||||||
get_mutations(PyObject* dict) {
|
get_mutations(PyObject* dict) {
|
||||||
assert(PyDict_CheckExact(dict));
|
assert(PyDict_CheckExact(dict));
|
||||||
PyDictObject *d = (PyDictObject *)dict;
|
PyDictObject *d = (PyDictObject *)dict;
|
||||||
return (d->ma_version_tag >> DICT_MAX_WATCHERS) & ((1 << DICT_WATCHED_MUTATION_BITS)-1);
|
return (d->_ma_watcher_tag >> DICT_MAX_WATCHERS) & ((1 << DICT_WATCHED_MUTATION_BITS)-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
increment_mutations(PyObject* dict) {
|
increment_mutations(PyObject* dict) {
|
||||||
assert(PyDict_CheckExact(dict));
|
assert(PyDict_CheckExact(dict));
|
||||||
PyDictObject *d = (PyDictObject *)dict;
|
PyDictObject *d = (PyDictObject *)dict;
|
||||||
d->ma_version_tag += (1 << DICT_MAX_WATCHERS);
|
d->_ma_watcher_tag += (1 << DICT_MAX_WATCHERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The first two dict watcher IDs are reserved for CPython,
|
/* The first two dict watcher IDs are reserved for CPython,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue