Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions Include/cpython/dictobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,11 @@ typedef struct {
/* Number of items in the dictionary */
Py_ssize_t ma_used;

/* Dictionary version: globally unique, value change each time
the dictionary is modified */
#ifdef Py_BUILD_CORE
/* Bits 0-7 are for dict watchers.
/* This is a private field for CPython's internal use.
* Bits 0-7 are for dict watchers.
* Bits 8-11 are for the watched mutation counter (used by tier2 optimization)
* The remaining bits (12-63) are the actual version tag. */
uint64_t ma_version_tag;
#else
Py_DEPRECATED(3.12) uint64_t ma_version_tag;
#endif
* The remaining bits are not currently used. */
uint64_t _ma_watcher_tag;

PyDictKeysObject *ma_keys;

Expand Down
30 changes: 2 additions & 28 deletions Include/internal/pycore_dict.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,31 +230,6 @@ static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) {
#define DICT_WATCHER_MASK ((1 << DICT_MAX_WATCHERS) - 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)
_PyDict_SendEvent(int watcher_bits,
Expand All @@ -263,20 +238,19 @@ _PyDict_SendEvent(int watcher_bits,
PyObject *key,
PyObject *value);

static inline uint64_t
static inline void
_PyDict_NotifyEvent(PyInterpreterState *interp,
PyDict_WatchEvent event,
PyDictObject *mp,
PyObject *key,
PyObject *value)
{
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) {
RARE_EVENT_STAT_INC(watched_dict_modification);
_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);
Expand Down
4 changes: 0 additions & 4 deletions Include/internal/pycore_dict_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ extern "C" {
#define DICT_WATCHED_MUTATION_BITS 4

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;
PyDict_WatchCallback watchers[DICT_MAX_WATCHERS];
};
Expand Down
191 changes: 0 additions & 191 deletions Lib/test/test_dict_version.py

This file was deleted.

35 changes: 0 additions & 35 deletions Lib/test/test_free_threading/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,41 +142,6 @@ def writer_func(l):
for ref in thread_list:
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__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -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.
14 changes: 0 additions & 14 deletions Modules/_testcapi/dict.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,19 +181,6 @@ dict_popstring_null(PyObject *self, PyObject *args)
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[] = {
{"dict_containsstring", dict_containsstring, METH_VARARGS},
{"dict_getitemref", dict_getitemref, METH_VARARGS},
Expand All @@ -204,7 +191,6 @@ static PyMethodDef test_methods[] = {
{"dict_pop_null", dict_pop_null, METH_VARARGS},
{"dict_popstring", dict_popstring, METH_VARARGS},
{"dict_popstring_null", dict_popstring_null, METH_VARARGS},
{"dict_version", dict_version, METH_O},
{NULL},
};

Expand Down
20 changes: 0 additions & 20 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1909,25 +1909,6 @@ getitem_with_error(PyObject *self, PyObject *args)
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 *
raise_SIGINT_then_send_None(PyObject *self, PyObject *args)
Expand Down Expand Up @@ -3407,7 +3388,6 @@ static PyMethodDef TestMethods[] = {
{"return_result_with_error", return_result_with_error, METH_NOARGS},
{"getitem_with_error", getitem_with_error, METH_VARARGS},
{"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},
{"stack_pointer", stack_pointer, METH_NOARGS},
#ifdef W_STOPCODE
Expand Down
Loading
Loading