-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Class objects become immortal and cannot be GC after creating a new thread in free-threaded Python #124239
Comments
My friend encountered the same bug! |
I was able to reproduce this behavior on 3.13, but this is probably a wontfix. Reference counts are, more or less, an implementation detail. On free threading, I'm guessing that type objects are immortalized to avoid reference count contention. (Types are something that are likely to be accessed across many threads, so immortalizing them allows threads to use an object without doing atomic reference count modifications.) We have a tradeoff to make when it comes to free threading. Should we hurt the performance of threads to support some users that relying on when type objects get garbage collected? Out of curiosity, is there something that's relying on type objects getting manually garbage collected? |
This is the intended behavior in 3.13. See the following issue for a description: In 3.14, type objects are not immortalized and are collected by the GC. |
Thanks for the information!
Let me share more context about this. I'm enabling free-threading support on a C extension. There is a I'm trying to test whether the paired The test log is here: https://github.com/metaopt/optree/actions/runs/10937390199/job/30363160663?pr=137 def test_unregister_pytree_node_memory_leak(): # noqa: C901
@optree.register_pytree_node_class(namespace=GLOBAL_NAMESPACE)
class MyList1(UserList):
def tree_flatten(self):
return self.data, None, None
@classmethod
def tree_unflatten(cls, metadata, children):
return cls(children)
wr = weakref.ref(MyList1)
assert wr() is not None
optree.unregister_pytree_node(MyList1, namespace=GLOBAL_NAMESPACE)
del MyList1
gc_collect()
> assert wr() is None
E AssertionError: assert <class 'test_registry.test_unregister_pytree_node_memory_leak.<locals>.MyList1'> is None
E + where <class 'test_registry.test_unregister_pytree_node_memory_leak.<locals>.MyList1'> = <weakref at 0x20036242b90; to 'abc.ABCMeta' at 0x20031870010 (MyList1)>() This test works fine in Python 3.7-3.13 and failed in 3.13t. There is one more thing I want to point out is that the test order matters. $ pytest tests/test_registry.py tests/test_free_threading.py # OK
$ pytest tests/test_free_threading.py tests/test_registry.py # FAILED |
You can use the |
Thanks for the hint. I'll try it then. |
Bug report
Bug description:
A simple reproduce script to print the refcount when there is main thread only and after creating a new thread.
On Python 3.13:
On Python 3.13t:
CPython versions tested on:
3.13
Operating systems tested on:
macOS
The text was updated successfully, but these errors were encountered: