Message 188875 - Python tracker (original) (raw)

Classes contain two kinds of cycles back to themselves:

  1. in their _mro list, the class itself is typically the first member.
  2. in the descriptors for various fields such as dict.

The problem here is that there is no way to break the cycle. A class that is dynamically created (e.g. in a function) and then not needed, will stick around until garbage collection is performed.

This happens in spite of attempts within the core to avoid such cycles. For instance, the type's tp_subclasses list contains to avoid a cycle between a baseclass and its parent.

A .py file demonstrating the problem is attached.

A patch is attached that resolves the issue:

  1. the mro tuple in the type object is "nerfed" to contain a Py_None reference in its first place, where it previously held the cyclic reference to the type object itself. This is then "fixed" in place where required. the mro attribute becomes a getter that duplicates the tuple.

  2. the descriptors are modified to hold a weak-reference to the target type, rather than a strong reference.

  3. Fix process cleanup. The thread state cannot be released until after the cleanup of e.g. PySet_Fini() because the freeing of objects in there requires the DUSTBIN_SAFE macros that require the thread state. Cleanup behaviour probably changed since objects go away on their own now.

  4. changes to test_gc.py in the testsuite reflecting the changed behaviour

The patched code passes all the testsuite.