[PEAK] memory leaks & stuff

Phillip J. Eby pje at telecommunity.com
Wed Sep 15 11:50:54 EDT 2004

At 05:06 PM 9/15/04 +0300, alexander smishlajev wrote:
>Yaroslav Samchuk wrote, at 14.09.2004 20:52:
>>On the last week we had discovered, that the application, which is
>>developed by our team was leaking memory.
>while hunting for this leak, i've got two questions:
>- derived conditions Not, Intersect and Union are not exported from 
>events.sources.  why?

Because they're not intended for direct use: use the ~, &, and | operators 
on existing condition objects instead.

>- what's wrong with the following script?

Your Ticker._doEventLoop is an infinite loop, so once you assemble a 
Ticker, its Task will run indefinitely, even if you drop all other 
references to that Ticker instance.  The Task still has a reference to the 
Ticker (the 'self' local variable) so it can never be garbage collected.

Therefore, your "leaking" version leaks Ticker instances, one per second.

So, you should be careful to have a way for Tasks to exit, if you intend to 
be able to do away with the object(s) they operate on.  If you look at e.g. 
peak.tools.supervisor, you'll see that child process objects' control Tasks 
loop only until the child process exits, so that the process object can be 
garbage collected after that.  The only Tasks that can safely be infinite 
loops in a long-running application are those which will have only a fixed 
number of instances, and for which no garbage collection is needed.  (For 
example, peak.tools.supervisor has a few top-level control tasks that are 
infinite loops, but they don't control any resources that need to be 
released before the application as a whole exits.)

>=== cut ===
>import gc
>from peak.api import binding, events
>from peak.running import commands, interfaces
>class Ticker(binding.Component):
>     delay = 1
>     evt = binding.Make(events.Condition)
>     eventLoop = binding.Obtain(events.IEventLoop)
>     def _doEventLoop(self):
>         while True:
>             yield self.eventLoop.sleep(self.delay)
>             events.resume()
>             self.evt.set(True)
>     _doEventLoop = binding.Make(events.taskFactory(_doEventLoop),
>         uponAssembly=True)
>class App(commands.AbstractCommand):
>     mainLoop = binding.Obtain(interfaces.IMainLoop)
>     def _doEventLoop(self):
>         while True:
>             yield self.evt; events.resume()
>             ### non-leaking:
>             #self.evt.set(False)
>             ### leaking:
>             self._delBinding("evt")
>             gc.collect()
>             print "Total objects:", len(gc.get_objects())
>     _doEventLoop = binding.Make(events.taskFactory(_doEventLoop),
>         uponAssembly=True)
>     evt = binding.Make(lambda self: Ticker(parentComponent=self,
>         delay=1).evt)
>     def _run(self):
>         return self.mainLoop.run()
>if __name__ == "__main__":
>     commands.runMain(App)
>=== cut ===

More information about the PEAK mailing list