From: Mike C. F. <mcf...@us...> - 2004-11-26 06:37:46
|
Update of /cvsroot/pydispatcher/dispatch In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1551 Modified Files: dispatcher.py setup.py Log Message: Fixes for some very slow memory leaks showing up in one of our applications. Includes addition to the test suite that asserts that everything has been cleaned up after *every* dispatcher test. This is again a back-reference table leak, hopefully the last one. Index: setup.py =================================================================== RCS file: /cvsroot/pydispatcher/dispatch/setup.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** setup.py 18 Oct 2004 04:22:58 -0000 1.5 --- setup.py 26 Nov 2004 06:37:33 -0000 1.6 *************** *** 86,90 **** setup ( name = "PyDispatcher", ! version = "1.0.1", description= "Multi-producer-multi-consumer signal dispatching mechanism", author = "Patrick K. O'Brien", --- 86,90 ---- setup ( name = "PyDispatcher", ! version = "1.0.2", description= "Multi-producer-multi-consumer signal dispatching mechanism", author = "Patrick K. O'Brien", Index: dispatcher.py =================================================================== RCS file: /cvsroot/pydispatcher/dispatch/dispatcher.py,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** dispatcher.py 18 Oct 2004 04:09:32 -0000 1.7 --- dispatcher.py 26 Nov 2004 06:37:33 -0000 1.8 *************** *** 157,179 **** except: pass try: - receiverID = id(receiver) current = sendersBack.get( receiverID ) if current is None: sendersBack[ receiverID ] = current = [] ! current.append(senderkey) except: pass ! receivers = [] ! if signals.has_key(signal): ! receivers = signals[signal] ! else: ! signals[signal] = receivers ! try: ! receivers.remove(receiver) ! except ValueError: ! pass receivers.append(receiver) def disconnect(receiver, signal=Any, sender=Any, weak=True): """Disconnect receiver from sender for signal --- 157,182 ---- except: pass + + receiverID = id(receiver) + # get current set, remove any current references to + # this receiver in the set, including back-references + if signals.has_key(signal): + receivers = signals[signal] + _removeOldBackRefs(senderkey, signal, receiver, receivers) + else: + receivers = signals[signal] = [] try: current = sendersBack.get( receiverID ) if current is None: sendersBack[ receiverID ] = current = [] ! if senderkey not in current: ! current.append(senderkey) except: pass ! receivers.append(receiver) + + def disconnect(receiver, signal=Any, sender=Any, weak=True): """Disconnect receiver from sender for signal *************** *** 209,213 **** senderkey = id(sender) try: ! receivers = connections[senderkey][signal] except KeyError: raise errors.DispatcherKeyError( --- 212,217 ---- senderkey = id(sender) try: ! signals = connections[senderkey] ! receivers = signals[signal] except KeyError: raise errors.DispatcherKeyError( *************** *** 218,222 **** ) try: ! receivers.remove(receiver) except ValueError: raise errors.DispatcherKeyError( --- 222,227 ---- ) try: ! # also removes from receivers ! _removeOldBackRefs(senderkey, signal, receiver, receivers) except ValueError: raise errors.DispatcherKeyError( *************** *** 268,272 **** else: yield receiver ! def getAllReceivers( sender = Any, signal = Any ): """Get list of all receivers from global tables --- 273,279 ---- else: yield receiver ! ! ! def getAllReceivers( sender = Any, signal = Any ): """Get list of all receivers from global tables *************** *** 411,414 **** --- 418,422 ---- def _removeSender(senderkey): """Remove senderkey from connections.""" + _removeBackrefs(senderkey) try: del connections[senderkey] *************** *** 420,421 **** --- 428,492 ---- except: pass + + def _removeBackrefs( senderkey): + """Remove all back-references to this senderkey""" + try: + signals = connections[senderkey] + except KeyError: + signals = None + else: + items = signals.items() + def allReceivers( ): + for signal,set in items: + for item in set: + yield item + for receiver in allReceivers(): + _killBackref( receiver, senderkey ) + + def _removeOldBackRefs(senderkey, signal, receiver, receivers): + """Kill old sendersBack references from receiver + + This guards against multiple registration of the same + receiver for a given signal and sender leaking memory + as old back reference records build up. + + Also removes old receiver instance from receivers + """ + try: + index = receivers.index(receiver) + # need to scan back references here and remove senderkey + except ValueError: + return False + else: + oldReceiver = receivers[index] + del receivers[index] + found = 0 + signals = connections.get(signal) + if signals is not None: + for sig,recs in connections.get(signal,{}).iteritems(): + if sig != signal: + for rec in recs: + if rec is oldReceiver: + found = 1 + break + if not found: + _killBackref( oldReceiver, senderkey ) + return True + return False + + + def _killBackref( receiver, senderkey ): + """Do the actual removal of back reference from receiver to senderkey""" + receiverkey = id(receiver) + set = sendersBack.get( receiverkey, () ) + while senderkey in set: + try: + set.remove( senderkey ) + except: + break + if not set: + try: + del sendersBack[ receiverkey ] + except KeyError: + pass + return True |