@@ -65,6 +65,8 @@ lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound);
6565static int
6666slot_tp_setattro (PyObject * self , PyObject * name , PyObject * value );
6767
68+ static inline PyTypeObject * subclass_from_ref (PyObject * ref );
69+
6870/*
6971 * finds the beginning of the docstring's introspection signature.
7072 * if present, returns a pointer pointing to the first '('.
@@ -309,12 +311,11 @@ PyType_Modified(PyTypeObject *type)
309311 Py_ssize_t i = 0 ;
310312 PyObject * ref ;
311313 while (PyDict_Next (subclasses , & i , NULL , & ref )) {
312- assert (PyWeakref_CheckRef (ref ));
313- PyObject * obj = PyWeakref_GET_OBJECT (ref );
314- if (obj == Py_None ) {
314+ PyTypeObject * subclass = subclass_from_ref (ref ); // borrowed
315+ if (subclass == NULL ) {
315316 continue ;
316317 }
317- PyType_Modified (_PyType_CAST ( obj ) );
318+ PyType_Modified (subclass );
318319 }
319320 }
320321
@@ -4211,23 +4212,48 @@ type_dealloc_common(PyTypeObject *type)
42114212}
42124213
42134214
4214- void
4215- _PyStaticType_Dealloc (PyTypeObject * type )
4215+ static void
4216+ clear_static_tp_subclasses (PyTypeObject * type )
42164217{
4217- // If a type still has subtypes, it cannot be deallocated.
4218- // A subtype can inherit attributes and methods of its parent type,
4219- // and a type must no longer be used once it's deallocated.
4220- if (type -> tp_subclasses != NULL ) {
4218+ if (type -> tp_subclasses == NULL ) {
42214219 return ;
42224220 }
42234221
4222+ /* Normally it would be a problem to finalize the type if its
4223+ tp_subclasses wasn't cleared first. However, this is only
4224+ ever called at the end of runtime finalization, so we can be
4225+ more liberal in cleaning up. If the given type still has
4226+ subtypes at this point then some extension module did not
4227+ correctly finalize its objects.
4228+
4229+ We can safely obliterate such subtypes since the extension
4230+ module and its objects won't be used again, except maybe if
4231+ the runtime were re-initialized. In that case the sticky
4232+ situation would only happen if the module were re-imported
4233+ then and only if the subtype were stored in a global and only
4234+ if that global were not overwritten during import. We'd be
4235+ fine since the extension is otherwise unsafe and unsupported
4236+ in that situation, and likely problematic already.
4237+
4238+ In any case, this situation means at least some memory is
4239+ going to leak. This mostly only affects embedding scenarios.
4240+ */
4241+
4242+ // For now we just clear tp_subclasses.
4243+
4244+ Py_CLEAR (type -> tp_subclasses );
4245+ }
4246+
4247+ void
4248+ _PyStaticType_Dealloc (PyTypeObject * type )
4249+ {
42244250 type_dealloc_common (type );
42254251
42264252 Py_CLEAR (type -> tp_dict );
42274253 Py_CLEAR (type -> tp_bases );
42284254 Py_CLEAR (type -> tp_mro );
42294255 Py_CLEAR (type -> tp_cache );
4230- // type->tp_subclasses is NULL
4256+ clear_static_tp_subclasses ( type );
42314257
42324258 // PyObject_ClearWeakRefs() raises an exception if Py_REFCNT() != 0
42334259 if (Py_REFCNT (type ) == 0 ) {
@@ -4296,14 +4322,12 @@ _PyType_GetSubclasses(PyTypeObject *self)
42964322 Py_ssize_t i = 0 ;
42974323 PyObject * ref ; // borrowed ref
42984324 while (PyDict_Next (subclasses , & i , NULL , & ref )) {
4299- assert (PyWeakref_CheckRef (ref ));
4300- PyObject * obj = PyWeakref_GET_OBJECT (ref ); // borrowed ref
4301- if (obj == Py_None ) {
4325+ PyTypeObject * subclass = subclass_from_ref (ref ); // borrowed
4326+ if (subclass == NULL ) {
43024327 continue ;
43034328 }
4304- assert (PyType_Check (obj ));
43054329
4306- if (PyList_Append (list , obj ) < 0 ) {
4330+ if (PyList_Append (list , _PyObject_CAST ( subclass ) ) < 0 ) {
43074331 Py_DECREF (list );
43084332 return NULL ;
43094333 }
@@ -6708,6 +6732,42 @@ add_all_subclasses(PyTypeObject *type, PyObject *bases)
67086732 return res ;
67096733}
67106734
6735+ static inline PyTypeObject *
6736+ subclass_from_ref (PyObject * ref )
6737+ {
6738+ assert (PyWeakref_CheckRef (ref ));
6739+ PyObject * obj = PyWeakref_GET_OBJECT (ref ); // borrowed ref
6740+ assert (obj != NULL );
6741+ if (obj == Py_None ) {
6742+ return NULL ;
6743+ }
6744+ assert (PyType_Check (obj ));
6745+ return _PyType_CAST (obj );
6746+ }
6747+
6748+ static PyObject *
6749+ get_subclasses_key (PyTypeObject * type , PyTypeObject * base )
6750+ {
6751+ PyObject * key = PyLong_FromVoidPtr ((void * ) type );
6752+ if (key != NULL ) {
6753+ return key ;
6754+ }
6755+ PyErr_Clear ();
6756+
6757+ /* This basically means we're out of memory.
6758+ We fall back to manually traversing the values. */
6759+ Py_ssize_t i = 0 ;
6760+ PyObject * ref ; // borrowed ref
6761+ while (PyDict_Next ((PyObject * )base -> tp_subclasses , & i , & key , & ref )) {
6762+ PyTypeObject * subclass = subclass_from_ref (ref ); // borrowed
6763+ if (subclass == type ) {
6764+ return Py_NewRef (key );
6765+ }
6766+ }
6767+ /* It wasn't found. */
6768+ return NULL ;
6769+ }
6770+
67116771static void
67126772remove_subclass (PyTypeObject * base , PyTypeObject * type )
67136773{
@@ -6717,8 +6777,8 @@ remove_subclass(PyTypeObject *base, PyTypeObject *type)
67176777 }
67186778 assert (PyDict_CheckExact (subclasses ));
67196779
6720- PyObject * key = PyLong_FromVoidPtr (( void * ) type );
6721- if (key == NULL || PyDict_DelItem (subclasses , key )) {
6780+ PyObject * key = get_subclasses_key ( type , base );
6781+ if (key != NULL && PyDict_DelItem (subclasses , key )) {
67226782 /* This can happen if the type initialization errored out before
67236783 the base subclasses were updated (e.g. a non-str __qualname__
67246784 was passed in the type dict). */
@@ -8811,13 +8871,10 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
88118871 Py_ssize_t i = 0 ;
88128872 PyObject * ref ;
88138873 while (PyDict_Next (subclasses , & i , NULL , & ref )) {
8814- assert (PyWeakref_CheckRef (ref ));
8815- PyObject * obj = PyWeakref_GET_OBJECT (ref );
8816- assert (obj != NULL );
8817- if (obj == Py_None ) {
8874+ PyTypeObject * subclass = subclass_from_ref (ref ); // borrowed
8875+ if (subclass == NULL ) {
88188876 continue ;
88198877 }
8820- PyTypeObject * subclass = _PyType_CAST (obj );
88218878
88228879 /* Avoid recursing down into unaffected classes */
88238880 PyObject * dict = subclass -> tp_dict ;
0 commit comments