@@ -138,6 +138,7 @@ As a consequence of this, split keys have a maximum size of 16.
138138// Forward declarations
139139static PyObject * frozendict_new (PyTypeObject * type , PyObject * args ,
140140 PyObject * kwds );
141+ static PyObject * frozendict_new_untracked (PyTypeObject * type );
141142static PyObject * dict_new (PyTypeObject * type , PyObject * args , PyObject * kwds );
142143static int dict_merge (PyObject * a , PyObject * b , int override , PyObject * * dupkey );
143144static int dict_contains (PyObject * op , PyObject * key );
@@ -5242,7 +5243,7 @@ static PyNumberMethods dict_as_number = {
52425243};
52435244
52445245static PyObject *
5245- dict_new (PyTypeObject * type , PyObject * args , PyObject * kwds )
5246+ dict_new_untracked (PyTypeObject * type )
52465247{
52475248 assert (type != NULL );
52485249 assert (type -> tp_alloc != NULL );
@@ -5262,8 +5263,18 @@ dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
52625263 d -> ma_keys = Py_EMPTY_KEYS ;
52635264 d -> ma_values = NULL ;
52645265 ASSERT_CONSISTENT (d );
5265- if (!_PyObject_GC_IS_TRACKED (d )) {
5266- _PyObject_GC_TRACK (d );
5266+ return self ;
5267+ }
5268+
5269+ static PyObject *
5270+ dict_new (PyTypeObject * type , PyObject * args , PyObject * kwds )
5271+ {
5272+ PyObject * self = dict_new_untracked (type );
5273+ if (self == NULL ) {
5274+ return NULL ;
5275+ }
5276+ if (!_PyObject_GC_IS_TRACKED (self )) {
5277+ _PyObject_GC_TRACK (self );
52675278 }
52685279 return self ;
52695280}
@@ -5323,7 +5334,9 @@ frozendict_vectorcall(PyObject *type, PyObject * const*args,
53235334 return Py_NewRef (args [0 ]);
53245335 }
53255336
5326- PyObject * self = frozendict_new (_PyType_CAST (type ), NULL , NULL );
5337+ /* Keep the frozendict untracked until it is fully built, so a half-built
5338+ object is never reachable from another thread. */
5339+ PyObject * self = frozendict_new_untracked (_PyType_CAST (type ));
53275340 if (self == NULL ) {
53285341 return NULL ;
53295342 }
@@ -5343,6 +5356,10 @@ frozendict_vectorcall(PyObject *type, PyObject * const*args,
53435356 }
53445357 }
53455358 }
5359+ /* Track only once fully built. */
5360+ if (!_PyObject_GC_IS_TRACKED (self )) {
5361+ _PyObject_GC_TRACK (self );
5362+ }
53465363 return self ;
53475364}
53485365
@@ -8361,17 +8378,28 @@ frozendict_hash(PyObject *op)
83618378}
83628379
83638380
8381+ /* Allocate an empty, GC-untracked frozendict. Staying untracked while it is
8382+ filled keeps a half-built frozendict so another thread can't observe it
8383+ changing. Callers must GC-track it once fully built. */
83648384static PyObject *
8365- frozendict_new (PyTypeObject * type , PyObject * args , PyObject * kwds )
8385+ frozendict_new_untracked (PyTypeObject * type )
83668386{
8367- PyObject * d = dict_new (type , args , kwds );
8387+ PyObject * d = dict_new_untracked (type );
83688388 if (d == NULL ) {
83698389 return NULL ;
83708390 }
83718391 assert (can_modify_dict (_PyAnyDict_CAST (d )));
8392+ _PyFrozenDictObject_CAST (d )-> ma_hash = -1 ;
8393+ return d ;
8394+ }
83728395
8373- PyFrozenDictObject * self = _PyFrozenDictObject_CAST (d );
8374- self -> ma_hash = -1 ;
8396+ static PyObject *
8397+ frozendict_new (PyTypeObject * type , PyObject * args , PyObject * kwds )
8398+ {
8399+ PyObject * d = frozendict_new_untracked (type );
8400+ if (d == NULL ) {
8401+ return NULL ;
8402+ }
83758403
83768404 if (args != NULL ) {
83778405 if (dict_update_common (d , args , kwds , "frozendict" ) < 0 ) {
@@ -8383,6 +8411,10 @@ frozendict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
83838411 assert (kwds == NULL );
83848412 }
83858413
8414+ /* Track only once fully built. */
8415+ if (!_PyObject_GC_IS_TRACKED (d )) {
8416+ _PyObject_GC_TRACK (d );
8417+ }
83868418 return d ;
83878419}
83888420
0 commit comments