codypiersall / pynng

Python bindings for Nanomsg Next Generation.
https://pynng.readthedocs.io
MIT License
267 stars 58 forks source link

Sockets don't seem to be garbage collected #90

Closed WJCFerguson closed 3 years ago

WJCFerguson commented 3 years ago

Even if I call close(), it seems they are never gc'd? e.g.:


>>> import pynng
>>> sock = pynng.Pub0()
>>> sock.listen("ipc:///tmp/foo")
<pynng.nng.Listener object at 0x7fbf024b6520>
>>> sock
<pynng.nng.Pub0 object at 0x7fbf024b66d0>
>>> import gc
>>> [o for o in gc.get_objects() if isinstance(o, pynng.Pub0)]
[<pynng.nng.Pub0 object at 0x7fbf024b66d0>]
>>> sock = None
>>> [o for o in gc.get_objects() if isinstance(o, pynng.Pub0)]
[<pynng.nng.Pub0 object at 0x7fbf024b66d0>]
>>> [o for o in gc.get_objects() if isinstance(o, pynng.Pub0)][0].close()
>>> [o for o in gc.get_objects() if isinstance(o, pynng.Pub0)]
[<pynng.nng.Pub0 object at 0x7fbf024b66d0>]
>>> gc.get_referrers([o for o in gc.get_objects() if isinstance(o, pynng.Pub0)][0])
[<cdata 'void *' handle to <pynng.nng.Pub0 object at 0x7fbf024b66d0>>, <frame at 0x7fbf023e1040, file '/home/james/src/software/volta/.venv/lib/python3.8/site-packages/pynng/nng.py', line 405, code listen>, [<pynng.nng.Pub0 object at 0x7fbf024b66d0>]]

Is this not a bug? Thanks.

WJCFerguson commented 3 years ago

Similar with context manager:

>>> with pynng.Pub0() as pub:
...   pass
>>> pub
<pynng.nng.Pub0 object at 0x7f006bdc2850>
>>> pub = None
>>> [o for o in gc.get_objects() if isinstance(o, pynng.Pub0)]
[<pynng.nng.Pub0 object at 0x7f006bdc2850>]
>>> 
>>> gc.get_referrers([o for o in gc.get_objects() if isinstance(o, pynng.Pub0)][0])
[<cdata 'void *' handle to <pynng.nng.Pub0 object at 0x7f006bdc2850>>, [<pynng.nng.Pub0 object at 0x7f006bdc2850>]]
codypiersall commented 3 years ago

Thank you for the detailed bug report, this is very helpful! I'll see if I can track this down.

codypiersall commented 3 years ago

Turns out there is a reference cycle, so the socket is not collected immediately, but when the full garbage collection is run the sockets are collected as they should be. I just added a new test to ensure that sockets are collected, and in the test I manually call gc.collect() to make sure that the sockets are collected.

Thanks for opening the issue!

WJCFerguson commented 3 years ago

Hey thanks for this, and sorry for the bother. I thought about gc.collect() after I sent this, but was away from the computer and forgot. It's a while since I was messing with garbage collection, but I should have known.