December 23, 2009

Dynamic Methods and Garbage Collecting

I really enjoy the Python programming language. Even when it bites you, it takes you on an interesting journey.

In the MUD, I use the term Client to refer to the session with the user at the other end of the wire. The Client is passed to a User object that represents the player in some state or another. When a User gets deleted it gets chomped up by Python's garbage collector which should delete the client which in turn should delete the socket which fires the socket's close() method on the way out and gives the cat a fish, err, drops the user resulting in the famous 'Connection closed by foreign host..'

Only it wasn't.

Deleted clients and "disconnected" users were hanging around typing to unmonitored, uncaring sockets. After a bit of poking I found the problem was some cheap hackery I was using for the login process. I was using a property called "cmd_driver" like a state machine, changing it to point to the next method I wanted input to go;
self.cmd_driver = self.get_password
Turns out, this was setting cmd_driver to a bound method; one that is wrapped inside a reference to the an instance of the class, which in this case was itself. This caused the reference count to increase by one and avoid the garbage collector.Here's a demonstration snippet;
>>> class A(object):
... def __del__(self):
... print "__del__ called"
... def foo(self):
... pass
...
>>> a = A()
>>> del a
__del__ called
>>> a2 = A()
>>> a2.bar = a2.foo
>>> del a2
>>>

So I need to work out a healthier version of the state machine.
Maybe one that calls unbound methods of the class or instance methods
via some introspection.

No comments: