@@ -305,16 +305,20 @@ static inline int
305305can_modify_dict (PyDictObject * mp )
306306{
307307 if (PyFrozenDict_Check (mp )) {
308+ // gh-151722: A frozendict must not be tracked by the GC
309+ // when it's being modified.
310+ assert (!_PyObject_GC_IS_TRACKED (mp ));
311+
308312 // No locking required to modify a newly created frozendict
309313 // since it's only accessible from the current thread.
310- return PyUnstable_Object_IsUniquelyReferenced (_PyObject_CAST (mp ));
314+ assert ( PyUnstable_Object_IsUniquelyReferenced (_PyObject_CAST (mp ) ));
311315 }
312316 else {
313317 // Locking is only required if the dictionary is not
314318 // uniquely referenced.
315319 ASSERT_DICT_LOCKED (mp );
316- return 1 ;
317320 }
321+ return 1 ;
318322}
319323#endif
320324
@@ -937,7 +941,7 @@ free_values(PyDictValues *values, bool use_qsbr)
937941static inline PyObject *
938942new_dict_impl (PyDictObject * mp , PyDictKeysObject * keys ,
939943 PyDictValues * values , Py_ssize_t used ,
940- int free_values_on_failure , int frozendict )
944+ int free_values_on_failure , int frozendict , int gc_track )
941945{
942946 assert (keys != NULL );
943947 if (mp == NULL ) {
@@ -956,12 +960,14 @@ new_dict_impl(PyDictObject *mp, PyDictKeysObject *keys,
956960 ((PyFrozenDictObject * )mp )-> ma_hash = -1 ;
957961 }
958962 ASSERT_CONSISTENT (mp );
959- _PyObject_GC_TRACK (mp );
963+ if (gc_track ) {
964+ _PyObject_GC_TRACK (mp );
965+ }
960966 return (PyObject * )mp ;
961967}
962968
963969/* Consumes a reference to the keys object */
964- static PyObject *
970+ static PyObject *
965971new_dict (PyDictKeysObject * keys , PyDictValues * values ,
966972 Py_ssize_t used , int free_values_on_failure )
967973{
@@ -971,16 +977,30 @@ new_dict(PyDictKeysObject *keys, PyDictValues *values,
971977 }
972978 assert (mp == NULL || Py_IS_TYPE (mp , & PyDict_Type ));
973979
974- return new_dict_impl (mp , keys , values , used , free_values_on_failure , 0 );
980+ return new_dict_impl (mp , keys , values , used , free_values_on_failure , 0 , 1 );
975981}
976982
977983/* Consumes a reference to the keys object */
978- static PyObject *
979- new_frozendict (PyDictKeysObject * keys , PyDictValues * values ,
980- Py_ssize_t used , int free_values_on_failure )
984+ static PyObject *
985+ new_dict_untracked (PyDictKeysObject * keys , PyDictValues * values ,
986+ Py_ssize_t used , int free_values_on_failure )
987+ {
988+ PyDictObject * mp = _Py_FREELIST_POP (PyDictObject , dicts );
989+ if (mp == NULL ) {
990+ mp = PyObject_GC_New (PyDictObject , & PyDict_Type );
991+ }
992+ assert (mp == NULL || Py_IS_TYPE (mp , & PyDict_Type ));
993+
994+ return new_dict_impl (mp , keys , values , used , free_values_on_failure , 0 , 0 );
995+ }
996+
997+ /* Consumes a reference to the keys object */
998+ static PyObject *
999+ new_frozendict_untracked (PyDictKeysObject * keys , PyDictValues * values ,
1000+ Py_ssize_t used , int free_values_on_failure )
9811001{
9821002 PyDictObject * mp = PyObject_GC_New (PyDictObject , & PyFrozenDict_Type );
983- return new_dict_impl (mp , keys , values , used , free_values_on_failure , 1 );
1003+ return new_dict_impl (mp , keys , values , used , free_values_on_failure , 1 , 0 );
9841004}
9851005
9861006static PyObject *
@@ -3471,7 +3491,6 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
34713491 }
34723492 if (PyFrozenDict_Check (d )) {
34733493 assert (can_modify_dict ((PyDictObject * )d ));
3474- assert (!_PyObject_GC_IS_TRACKED (d ));
34753494 }
34763495
34773496 if (PyDict_CheckExact (d )) {
@@ -4076,6 +4095,7 @@ merge_from_seq2_lock_held(PyObject *d, PyObject *seq2, int override)
40764095 assert (d != NULL );
40774096 assert (PyAnyDict_Check (d ));
40784097 assert (seq2 != NULL );
4098+ assert (can_modify_dict ((PyDictObject * )d ));
40794099
40804100 it = PyObject_GetIter (seq2 );
40814101 if (it == NULL )
@@ -4277,6 +4297,8 @@ dict_merge(PyObject *a, PyObject *b, int override, PyObject **dupkey)
42774297 assert (0 <= override && override <= 2 );
42784298
42794299 PyDictObject * mp = _PyAnyDict_CAST (a );
4300+ assert (can_modify_dict (mp ));
4301+
42804302 int res = 0 ;
42814303 if (PyAnyDict_Check (b ) && (Py_TYPE (b )-> tp_iter == dict_iter )) {
42824304 PyDictObject * other = (PyDictObject * )b ;
@@ -4382,9 +4404,7 @@ dict_merge_api(PyObject *a, PyObject *b, int override, PyObject **dupkey)
43824404 }
43834405
43844406 int res = dict_merge (a , b , override , dupkey );
4385- if (PyDict_Check (a )) {
4386- assert (_PyObject_GC_IS_TRACKED (a ));
4387- }
4407+ assert (_PyObject_GC_IS_TRACKED (a ));
43884408 return res ;
43894409}
43904410
@@ -4442,7 +4462,7 @@ copy_values(PyDictValues *values)
44424462}
44434463
44444464static PyObject *
4445- copy_lock_held (PyObject * o , int as_frozendict )
4465+ copy_lock_held_untracked (PyObject * o , int as_frozendict )
44464466{
44474467 PyObject * copy ;
44484468 PyDictObject * mp ;
@@ -4455,12 +4475,15 @@ copy_lock_held(PyObject *o, int as_frozendict)
44554475 mp = (PyDictObject * )o ;
44564476 if (mp -> ma_used == 0 ) {
44574477 /* The dict is empty; just return a new dict. */
4478+ PyObject * d ;
44584479 if (as_frozendict ) {
4459- return PyFrozenDict_New ( NULL );
4480+ d = frozendict_new_untracked ( & PyFrozenDict_Type );
44604481 }
44614482 else {
4462- return PyDict_New ( );
4483+ d = dict_new_untracked ( & PyDict_Type );
44634484 }
4485+ assert (!_PyObject_GC_IS_TRACKED (d ));
4486+ return d ;
44644487 }
44654488
44664489 if (_PyDict_HasSplitTable (mp )) {
@@ -4492,7 +4515,7 @@ copy_lock_held(PyObject *o, int as_frozendict)
44924515 PyFrozenDictObject * frozen = (PyFrozenDictObject * )split_copy ;
44934516 frozen -> ma_hash = -1 ;
44944517 }
4495- _PyObject_GC_TRACK ( split_copy );
4518+ assert (! _PyObject_GC_IS_TRACKED ( split_copy ) );
44964519 return (PyObject * )split_copy ;
44974520 }
44984521
@@ -4520,10 +4543,10 @@ copy_lock_held(PyObject *o, int as_frozendict)
45204543 }
45214544 PyDictObject * new ;
45224545 if (as_frozendict ) {
4523- new = (PyDictObject * )new_frozendict (keys , NULL , 0 , 0 );
4546+ new = (PyDictObject * )new_frozendict_untracked (keys , NULL , 0 , 0 );
45244547 }
45254548 else {
4526- new = (PyDictObject * )new_dict (keys , NULL , 0 , 0 );
4549+ new = (PyDictObject * )new_dict_untracked (keys , NULL , 0 , 0 );
45274550 }
45284551 if (new == NULL ) {
45294552 /* In case of an error, new_dict()/new_frozendict() takes care of
@@ -4533,14 +4556,15 @@ copy_lock_held(PyObject *o, int as_frozendict)
45334556
45344557 new -> ma_used = mp -> ma_used ;
45354558 ASSERT_CONSISTENT (new );
4559+ assert (!_PyObject_GC_IS_TRACKED (new ));
45364560 return (PyObject * )new ;
45374561 }
45384562
45394563 if (as_frozendict ) {
4540- copy = PyFrozenDict_New ( NULL );
4564+ copy = frozendict_new_untracked ( & PyFrozenDict_Type );
45414565 }
45424566 else {
4543- copy = PyDict_New ( );
4567+ copy = dict_new_untracked ( & PyDict_Type );
45444568 }
45454569 if (copy == NULL )
45464570 return NULL ;
@@ -4549,9 +4573,7 @@ copy_lock_held(PyObject *o, int as_frozendict)
45494573 return NULL ;
45504574 }
45514575
4552- if (PyDict_Check (copy )) {
4553- assert (_PyObject_GC_IS_TRACKED (copy ));
4554- }
4576+ assert (!_PyObject_GC_IS_TRACKED (copy ));
45554577 return copy ;
45564578}
45574579
@@ -4565,25 +4587,28 @@ PyDict_Copy(PyObject *o)
45654587
45664588 PyObject * res ;
45674589 Py_BEGIN_CRITICAL_SECTION (o );
4568- res = copy_lock_held (o , 0 );
4590+ res = copy_lock_held_untracked (o , 0 );
45694591 Py_END_CRITICAL_SECTION ();
4592+ if (res != NULL ) {
4593+ _PyObject_GC_TRACK (res );
4594+ }
45704595 return res ;
45714596}
45724597
45734598// Similar to PyDict_Copy(), but return a frozendict if the argument
45744599// is a frozendict.
45754600static PyObject *
4576- anydict_copy (PyObject * o )
4601+ anydict_copy_untracked (PyObject * o )
45774602{
45784603 assert (PyAnyDict_Check (o ));
45794604
45804605 PyObject * res ;
45814606 if (PyFrozenDict_Check (o )) {
4582- res = copy_lock_held (o , 1 );
4607+ res = copy_lock_held_untracked (o , 1 );
45834608 }
45844609 else {
45854610 Py_BEGIN_CRITICAL_SECTION (o );
4586- res = copy_lock_held (o , 0 );
4611+ res = copy_lock_held_untracked (o , 0 );
45874612 Py_END_CRITICAL_SECTION ();
45884613 }
45894614 return res ;
@@ -4598,13 +4623,16 @@ _PyDict_CopyAsDict(PyObject *o)
45984623
45994624 PyObject * res ;
46004625 if (PyFrozenDict_Check (o )) {
4601- res = copy_lock_held (o , 0 );
4626+ res = copy_lock_held_untracked (o , 0 );
46024627 }
46034628 else {
46044629 Py_BEGIN_CRITICAL_SECTION (o );
4605- res = copy_lock_held (o , 0 );
4630+ res = copy_lock_held_untracked (o , 0 );
46064631 Py_END_CRITICAL_SECTION ();
46074632 }
4633+ if (res != NULL ) {
4634+ _PyObject_GC_TRACK (res );
4635+ }
46084636 return res ;
46094637}
46104638
@@ -5157,14 +5185,15 @@ _PyDict_Or(PyObject *self, PyObject *other)
51575185 if (!PyAnyDict_Check (self ) || !PyAnyDict_Check (other )) {
51585186 Py_RETURN_NOTIMPLEMENTED ;
51595187 }
5160- PyObject * new = anydict_copy (self );
5188+ PyObject * new = anydict_copy_untracked (self );
51615189 if (new == NULL ) {
51625190 return NULL ;
51635191 }
51645192 if (dict_update_arg (new , other )) {
51655193 Py_DECREF (new );
51665194 return NULL ;
51675195 }
5196+ _PyObject_GC_TRACK (new );
51685197 return new ;
51695198}
51705199
@@ -5352,7 +5381,6 @@ dict_new(PyTypeObject *type, PyObject *Py_UNUSED(args), PyObject *Py_UNUSED(kwds
53525381 if (self == NULL ) {
53535382 return NULL ;
53545383 }
5355- assert (!_PyObject_GC_IS_TRACKED (self ));
53565384 _PyObject_GC_TRACK (self );
53575385 return self ;
53585386}
@@ -5434,7 +5462,7 @@ frozendict_vectorcall(PyObject *type, PyObject * const*args,
54345462 }
54355463 }
54365464 }
5437- assert (! _PyObject_GC_IS_TRACKED ( self ));
5465+
54385466 _PyObject_GC_TRACK (self );
54395467 return self ;
54405468}
@@ -6753,10 +6781,12 @@ dictitems_xor_lock_held(PyObject *d1, PyObject *d2)
67536781 ASSERT_DICT_LOCKED (d1 );
67546782 ASSERT_DICT_LOCKED (d2 );
67556783
6756- PyObject * temp_dict = copy_lock_held (d1 , 0 );
6784+ PyObject * temp_dict = copy_lock_held_untracked (d1 , 0 );
67576785 if (temp_dict == NULL ) {
67586786 return NULL ;
67596787 }
6788+ _PyObject_GC_TRACK (temp_dict );
6789+
67606790 PyObject * result_set = PySet_New (NULL );
67616791 if (result_set == NULL ) {
67626792 Py_CLEAR (temp_dict );
@@ -8488,7 +8518,6 @@ frozendict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
84888518 assert (kwds == NULL );
84898519 }
84908520
8491- assert (!_PyObject_GC_IS_TRACKED (d ));
84928521 _PyObject_GC_TRACK (d );
84938522 return d ;
84948523}
@@ -8533,7 +8562,11 @@ frozendict_copy_impl(PyFrozenDictObject *self)
85338562 return Py_NewRef (self );
85348563 }
85358564
8536- return anydict_copy ((PyObject * )self );
8565+ PyObject * copy = anydict_copy_untracked ((PyObject * )self );
8566+ if (copy != NULL ) {
8567+ _PyObject_GC_TRACK (copy );
8568+ }
8569+ return copy ;
85378570}
85388571
85398572
0 commit comments