@@ -11,6 +11,7 @@ extern "C" {
1111
1212#include "pycore_dict_state.h"
1313#include "pycore_runtime.h" // _PyRuntime
14+ #include "pycore_pystate.h" // _PyThreadState_GET()
1415
1516
1617/* runtime lifecycle */
@@ -22,9 +23,9 @@ extern void _PyDict_Fini(PyInterpreterState *interp);
2223
2324typedef struct {
2425 /* Cached hash code of me_key. */
25- Py_hash_t me_hash ;
2626 PyObject * me_key ;
2727 PyObject * me_value ; /* This field is only meaningful for combined tables */
28+ Py_hash_t me_hash ;
2829} PyDictKeyEntry ;
2930
3031typedef struct {
@@ -64,12 +65,13 @@ extern PyObject *_PyDict_Pop_KnownHash(PyObject *, PyObject *, Py_hash_t, PyObje
6465typedef enum {
6566 DICT_KEYS_GENERAL = 0 ,
6667 DICT_KEYS_UNICODE = 1 ,
67- DICT_KEYS_SPLIT = 2
68+ DICT_KEYS_SPLIT = 2 ,
6869} DictKeysKind ;
6970
7071/* See dictobject.c for actual layout of DictKeysObject */
7172struct _dictkeysobject {
72- Py_ssize_t dk_refcnt ;
73+ /* Mutex to prevent concurrent modification of shared keys. */
74+ _PyMutex dk_mutex ;
7375
7476 /* Size of the hash table (dk_indices). It must be a power of 2. */
7577 uint8_t dk_log2_size ;
@@ -108,6 +110,13 @@ struct _dictkeysobject {
108110 see the DK_ENTRIES() macro */
109111};
110112
113+ typedef struct PyDictSharedKeysObject {
114+ uint8_t tracked ;
115+ uint8_t marked ;
116+ struct PyDictSharedKeysObject * next ;
117+ struct _dictkeysobject keys ;
118+ } PyDictSharedKeysObject ;
119+
111120/* This must be no more than 250, for the prefix size to fit in one byte. */
112121#define SHARED_KEYS_MAX_SIZE 30
113122#define NEXT_LOG2_SHARED_KEYS_MAX_SIZE 6
@@ -142,14 +151,31 @@ static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) {
142151 assert (dk -> dk_kind != DICT_KEYS_GENERAL );
143152 return (PyDictUnicodeEntry * )_DK_ENTRIES (dk );
144153}
154+ static inline PyDictSharedKeysObject * DK_AS_SPLIT (PyDictKeysObject * dk ) {
155+ assert (dk -> dk_kind == DICT_KEYS_SPLIT );
156+ char * mem = (char * )dk - offsetof(PyDictSharedKeysObject , keys );
157+ return (PyDictSharedKeysObject * )mem ;
158+ }
145159
146160#define DK_IS_UNICODE (dk ) ((dk)->dk_kind != DICT_KEYS_GENERAL)
147161
148162#define DICT_VERSION_INCREMENT (1 << DICT_MAX_WATCHERS)
163+ #define DICT_GLOBAL_VERSION_INCREMENT (DICT_VERSION_INCREMENT * 256)
149164#define DICT_VERSION_MASK (DICT_VERSION_INCREMENT - 1)
150165
151- #define DICT_NEXT_VERSION () \
152- (_PyRuntime.dict_state.global_version += DICT_VERSION_INCREMENT)
166+ static inline uint64_t
167+ _PyDict_NextVersion (PyThreadState * tstate )
168+ {
169+ uint64_t version = tstate -> dict_state .dict_version ;
170+ if (version % DICT_GLOBAL_VERSION_INCREMENT == 0 ) {
171+ version = _Py_atomic_add_uint64 (
172+ & _PyRuntime .dict_state .global_version ,
173+ DICT_GLOBAL_VERSION_INCREMENT );
174+ }
175+ version += DICT_VERSION_INCREMENT ;
176+ tstate -> dict_state .dict_version = version ;
177+ return version ;
178+ }
153179
154180void
155181_PyDict_SendEvent (int watcher_bits ,
@@ -167,9 +193,9 @@ _PyDict_NotifyEvent(PyDict_WatchEvent event,
167193 int watcher_bits = mp -> ma_version_tag & DICT_VERSION_MASK ;
168194 if (watcher_bits ) {
169195 _PyDict_SendEvent (watcher_bits , event , mp , key , value );
170- return DICT_NEXT_VERSION ( ) | watcher_bits ;
196+ return _PyDict_NextVersion ( _PyThreadState_GET () ) | watcher_bits ;
171197 }
172- return DICT_NEXT_VERSION ( );
198+ return _PyDict_NextVersion ( _PyThreadState_GET () );
173199}
174200
175201extern PyObject * _PyObject_MakeDictFromInstanceAttributes (PyObject * obj , PyDictValues * values );
@@ -184,7 +210,7 @@ _PyDictValues_AddToInsertionOrder(PyDictValues *values, Py_ssize_t ix)
184210 assert (ix < SHARED_KEYS_MAX_SIZE );
185211 uint8_t * size_ptr = ((uint8_t * )values )- 2 ;
186212 int size = * size_ptr ;
187- assert (size + 2 < ((uint8_t * )values )[-1 ]);
213+ assert (size < ((uint8_t * )values )[-1 ]);
188214 size ++ ;
189215 size_ptr [- size ] = (uint8_t )ix ;
190216 * size_ptr = size ;
0 commit comments