many bug fixes, almost completed

This commit is contained in:
Darren Ranalli 2007-04-06 11:14:07 +00:00
parent 5e7eeef6e2
commit 88950518ab

View File

@ -2,7 +2,15 @@ from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.PythonUtil import Queue, invertDictLossless from direct.showbase.PythonUtil import Queue, invertDictLossless
from direct.showbase.PythonUtil import itype, serialNum, safeRepr from direct.showbase.PythonUtil import itype, serialNum, safeRepr
from direct.showbase.Job import Job from direct.showbase.Job import Job
import types, weakref, random, __builtin__ import types, weakref, gc, random, __builtin__
def _createContainerLeak():
def leakContainer(task):
if not hasattr(simbase, 'leakContainer'):
simbase.leakContainer = []
simbase.leakContainer.append(1)
return task.cont
taskMgr.add(leakContainer, 'leakContainer-%s' % serialNum())
class CheckContainers(Job): class CheckContainers(Job):
""" """
@ -13,13 +21,18 @@ class CheckContainers(Job):
self._leakDetector = leakDetector self._leakDetector = leakDetector
self.notify = self._leakDetector.notify self.notify = self._leakDetector.notify
self._index = index self._index = index
ContainerLeakDetector.addPrivateId(self.__dict__)
def destroy(self):
ContainerLeakDetector.removePrivateId(self.__dict__)
Job.destroy(self)
def getPriority(self): def getPriority(self):
return Job.Priorities.Normal return Job.Priorities.Normal
def run(self): def run(self):
self._leakDetector._index2containerName2len[self._index] = {} self._leakDetector._index2containerId2len[self._index] = {}
self._leakDetector.notify.debug(repr(self._leakDetector._id2ref)) #self._leakDetector.notify.debug(repr(self._leakDetector._id2ref))
ids = self._leakDetector.getContainerIds() ids = self._leakDetector.getContainerIds()
# record the current len of each container # record the current len of each container
for id in ids: for id in ids:
@ -28,47 +41,89 @@ class CheckContainers(Job):
container = self._leakDetector.getContainerById(id) container = self._leakDetector.getContainerById(id)
except Exception, e: except Exception, e:
# this container no longer exists # this container no longer exists
self.notify.debug('container %s no longer exists; caught exception in getContainerById (%s)' % (name, e)) self.notify.debug('container %s no longer exists; caught exception in getContainerById (%s)' % (
self._leakDetector.getContainerNameById(id), e))
self._leakDetector.removeContainerById(id) self._leakDetector.removeContainerById(id)
continue continue
if container is None: if container is None:
# this container no longer exists # this container no longer exists
self.notify.debug('container %s no longer exists; getContainerById returned None (%s)' % (name, e)) self.notify.debug('container %s no longer exists; getContainerById returned None' %
self._leakDetector.getContainerNameById(id))
self._leakDetector.removeContainerById(id) self._leakDetector.removeContainerById(id)
continue continue
cLen = len(container) try:
name = self._leakDetector.getContainerNameById(id) cLen = len(container)
self._leakDetector._index2containerName2len[self._index][name] = cLen except Exception, e:
# this container no longer exists
self.notify.debug('%s is no longer a container, it is now %s (%s)' %
(self._leakDetector.getContainerNameById(id), safeRepr(container), e))
self._leakDetector.removeContainerById(id)
continue
self._leakDetector._index2containerId2len[self._index][id] = cLen
# compare the current len of each container to past lens # compare the current len of each container to past lens
if self._index > 0: if self._index > 0:
idx2name2len = self._leakDetector._index2containerName2len idx2id2len = self._leakDetector._index2containerId2len
for name in idx2name2len[self._index]: for id in idx2id2len[self._index]:
yield None yield None
if name in idx2name2len[self._index-1]: if id in idx2id2len[self._index-1]:
diff = idx2name2len[self._index][name] - idx2name2len[self._index-1][name] diff = idx2id2len[self._index][id] - idx2id2len[self._index-1][id]
if diff > 0: if diff > 0:
if diff > idx2name2len[self._index-1][name]: if diff > idx2id2len[self._index-1][id]:
minutes = (self._leakDetector._index2delay[self._index] - minutes = (self._leakDetector._index2delay[self._index] -
self._leakDetector._index2delay[self._index-1]) / 60. self._leakDetector._index2delay[self._index-1]) / 60.
name = self._leakDetector.getContainerNameById(id)
self.notify.warning('container %s grew > 200%% in %s minutes' % (name, minutes)) self.notify.warning('container %s grew > 200%% in %s minutes' % (name, minutes))
if self._index > 3: if (self._index > 3 and
diff2 = idx2name2len[self._index-1][name] - idx2name2len[self._index-2][name] id in idx2id2len[self._index-2] and
diff3 = idx2name2len[self._index-2][name] - idx2name2len[self._index-3][name] id in idx2id2len[self._index-3]):
diff2 = idx2id2len[self._index-1][id] - idx2id2len[self._index-2][id]
diff3 = idx2id2len[self._index-2][id] - idx2id2len[self._index-3][id]
if self._index <= 5: if self._index <= 5:
msg = ('%s consistently increased in length over the last 3 periods (currently %s items)' % if diff > 0 and diff2 > 0 and diff3 > 0:
(name, idx2name2len[self._index][name])) name = self._leakDetector.getContainerNameById(id)
self.notify.warning(msg) msg = ('%s consistently increased in length over the last 3 periods (currently %s items)' %
else: (name, idx2id2len[self._index][id]))
self.notify.warning(msg)
elif (id in idx2id2len[self._index-4] and
id in idx2id2len[self._index-5]):
# if size has consistently increased over the last 5 checks, send out a warning # if size has consistently increased over the last 5 checks, send out a warning
diff4 = idx2name2len[self._index-3][name] - idx2name2len[self._index-4][name] diff4 = idx2id2len[self._index-3][id] - idx2id2len[self._index-4][id]
diff5 = idx2name2len[self._index-4][name] - idx2name2len[self._index-5][name] diff5 = idx2id2len[self._index-4][id] - idx2id2len[self._index-5][id]
if diff > 0 and diff2 > 0 and diff3 > 0 and diff4 > 0 and diff5 > 0: if diff > 0 and diff2 > 0 and diff3 > 0 and diff4 > 0 and diff5 > 0:
name = self._leakDetector.getContainerNameById(id)
msg = ('%s consistently increased in length over the last 5 periods (currently %s items), notifying system' % msg = ('%s consistently increased in length over the last 5 periods (currently %s items), notifying system' %
(name, idx2name2len[self._index][name])) (name, idx2id2len[self._index][id]))
self.notify.warning(msg) self.notify.warning(msg)
messenger.send(self._leakDetector.getLeakEvent(), [msg]) messenger.send(self._leakDetector.getLeakEvent(), [msg])
yield Job.Done yield Job.Done
class PruneContainerRefs(Job):
"""
Job to destroy any container refs that have Indirections that are holding references
to objects that should be garbage-collected
"""
def __init__(self, name, leakDetector):
Job.__init__(self, name)
self._leakDetector = leakDetector
self.notify = self._leakDetector.notify
self._index = index
ContainerLeakDetector.addPrivateId(self.__dict__)
def destroy(self):
ContainerLeakDetector.removePrivateId(self.__dict__)
Job.destroy(self)
def getPriority(self):
return Job.Priorities.Normal
def run(self):
ids = self._leakDetector._id2ref.keys()
for id in ids:
yield None
ref = self._leakDetector._id2ref[id]
ref.destroyIfGarbageDictKey()
yield Job.Done
class NoDictKey: class NoDictKey:
pass pass
@ -76,13 +131,14 @@ class Indirection:
# represents the indirection that brings you from a container to an element of the container # represents the indirection that brings you from a container to an element of the container
# stored as a string to be used as part of an eval # stored as a string to be used as part of an eval
# each dictionary dereference is individually eval'd since the dict key might have been garbage-collected # each dictionary dereference is individually eval'd since the dict key might have been garbage-collected
class GarbageCollectedDictKey(Exception):
pass
def __init__(self, evalStr=None, dictKey=NoDictKey): def __init__(self, evalStr=None, dictKey=NoDictKey):
# if this is a dictionary lookup, pass dictKey instead of evalStr # if this is a dictionary lookup, pass dictKey instead of evalStr
self.evalStr = evalStr self.evalStr = evalStr
self.dictKey = NoDictKey self.dictKey = NoDictKey
# is the dictKey a weak reference?
self._isWeakRef = False
self._refCount = 0
if dictKey is not NoDictKey: if dictKey is not NoDictKey:
# if we can repr/eval the key, store it as an evalStr # if we can repr/eval the key, store it as an evalStr
keyRepr = repr(dictKey) keyRepr = repr(dictKey)
@ -100,34 +156,74 @@ class Indirection:
# eval/repr succeeded, store as an evalStr # eval/repr succeeded, store as an evalStr
self.evalStr = '[%s]' % keyRepr self.evalStr = '[%s]' % keyRepr
else: else:
# store a weakref to the key try:
self.dictKey = weakref.ref(dictKey) # store a weakref to the key
self.dictKey = weakref.ref(dictKey)
self._isWeakRef = True
except TypeError, e:
self.dictKey = dictKey
self._isWeakRef = False
def destroy(self):
del self.dictKey
def acquire(self):
self._refCount += 1
def release(self):
self._refCount -= 1
if self._refCount == 0:
self.destroy()
def isDictKey(self): def isDictKey(self):
# is this an indirection through a dictionary?
return self.dictKey is not NoDictKey return self.dictKey is not NoDictKey
def isGarbageDictKey(self):
# are we holding a non-weak reference to an object that should be
# garbage-collected?
if self.isDictKey() and not self._isWeakRef:
referrers = gc.get_referrers(self.dictKey)
print referrers
import pdb;pdb.set_trace()
def _getNonWeakDictKey(self):
if not self._isWeakRef:
return self.dictKey
else:
key = self.dictKey()
if key is None:
return '<garbage-collected dict key>'
return key
def dereferenceDictKey(self, parentDict): def dereferenceDictKey(self, parentDict):
key = self.dictKey() # look ourselves up in parentDict
if key is None: key = self._getNonWeakDictKey()
raise Indirection.GarbageCollectedDictKey() # objects in __builtin__ will have parentDict==None
if parentDict is None:
return key
return parentDict[key] return parentDict[key]
def getString(self, nextIndirection=None): def getString(self, prevIndirection=None, nextIndirection=None):
# return our contribution to the name of the object # return our contribution to the full name of an object
instanceDictStr = '.__dict__'
if self.evalStr is not None: if self.evalStr is not None:
# if we're an instance dict and the next indirection is not a dict key, # if we're an instance dict, skip over this one (obj.__dict__[keyName] == obj.keyName)
# skip over this one (obj.__dict__[keyName] == obj.keyName) if nextIndirection is not None and self.evalStr[-len(instanceDictStr):] == instanceDictStr:
if nextIndirection is not None and self.evalStr == '.__dict__': return self.evalStr[:-len(instanceDictStr)]
return '' # if the previous indirection was an instance dict, change our syntax from ['key'] to .key
if prevIndirection is not None and prevIndirection.evalStr is not None:
if prevIndirection.evalStr[-len(instanceDictStr):] == instanceDictStr:
return '.%s' % self.evalStr[2:-2]
return self.evalStr return self.evalStr
# we're stored as a dict key # we're stored as a dict key
# this might not eval, but that's OK, we're not using this string to find # this might not eval, but that's OK, we're not using this string to find
# the object, we dereference the parent dict # the object, we dereference the parent dict
key = self.dictKey() keyRepr = safeRepr(self._getNonWeakDictKey())
if key is None: # if the previous indirection was an instance dict, change our syntax from ['key'] to .key
return '<garbage-collected dict key>' if prevIndirection is not None and prevIndirection.evalStr is not None:
return safeRepr(key) if prevIndirection.evalStr[-len(instanceDictStr):] == instanceDictStr:
return '.%s' % keyRepr
return '[%s]' % keyRepr
def __repr__(self): def __repr__(self):
return self.getString() return self.getString()
@ -140,21 +236,30 @@ class ContainerRef:
""" """
class FailedEval(Exception): class FailedEval(Exception):
pass pass
# whatever this is set to will be the default ContainerRef
BaseRef = None
def __init__(self, other=None, indirection=None): def __init__(self, indirection, other=None):
self._indirections = [] self._indirections = []
# if no other passed in, try ContainerRef.BaseRef # if no other passed in, try ContainerRef.BaseRef
if other is None:
other = ContainerRef.BaseRef
if other is not None: if other is not None:
for ind in other._indirections: for ind in other._indirections:
self.addIndirection(ind) self.addIndirection(ind)
if indirection: self.addIndirection(indirection)
self.addIndirection(indirection)
def destroy(self):
for indirection in self._indirections:
indirection.release()
del self._indirections
def destroyIfGarbageDictKey(self):
# if any of our indirections are holding onto objects that
# should be garbage-collected, destroy
for indirection in self._indirections:
if indirection.isGarbageDictKey():
self.destroy()
return
def addIndirection(self, indirection): def addIndirection(self, indirection):
indirection.acquire()
self._indirections.append(indirection) self._indirections.append(indirection)
def _getContainerByEval(self, evalStr): def _getContainerByEval(self, evalStr):
@ -177,8 +282,6 @@ class ContainerRef:
#import pdb;pdb.set_trace() #import pdb;pdb.set_trace()
evalStr = '' evalStr = ''
curObj = None curObj = None
curIndirection = None
nextIndirection = None
for indirection in self._indirections: for indirection in self._indirections:
if not indirection.isDictKey(): if not indirection.isDictKey():
# build up a string to be eval'd # build up a string to be eval'd
@ -195,15 +298,21 @@ class ContainerRef:
def __repr__(self): def __repr__(self):
str = '' str = ''
prevIndirection = None
curIndirection = None curIndirection = None
nextIndirection = None nextIndirection = None
for i in xrange(len(self._indirections)): for i in xrange(len(self._indirections)):
if i > 0:
prevIndirection = self._indirections[i-1]
else:
prevIndirection = None
curIndirection = self._indirections[i] curIndirection = self._indirections[i]
if i < len(self._indirections)-1: if i < len(self._indirections)-1:
nextIndirection = self._indirections[i+1] nextIndirection = self._indirections[i+1]
else: else:
nextIndirection = None nextIndirection = None
str += curIndirection.getString(nextIndirection=nextIndirection) str += curIndirection.getString(prevIndirection=prevIndirection,
nextIndirection=nextIndirection)
return str return str
class ContainerLeakDetector(Job): class ContainerLeakDetector(Job):
@ -226,33 +335,39 @@ class ContainerLeakDetector(Job):
if firstCheckDelay is None: if firstCheckDelay is None:
firstCheckDelay = 60. * 15. firstCheckDelay = 60. * 15.
self._nextCheckDelay = firstCheckDelay self._nextCheckDelay = firstCheckDelay
self._index2containerName2len = {} self._pruneTaskPeriod = config.GetFloat('leak-detector-prune-period', 60. * 30.)
self._index2containerId2len = {}
self._index2delay = {} self._index2delay = {}
# set up our data structures # set up our data structures
self._id2ref = {} self._id2ref = {}
# set up the base/starting object # set up the base/starting object
self._nameContainer(__builtin__.__dict__, ContainerRef(indirection=Indirection(evalStr='__builtin__.__dict__'))) self._baseObjRef = ContainerRef(Indirection(evalStr='__builtin__.__dict__'))
self._nameContainer(__builtin__.__dict__, self._baseObjRef)
try: try:
base base
except: except:
pass pass
else: else:
ContainerRef.BaseRef = ContainerRef(indirection=Indirection(evalStr='base.__dict__')) self._baseObjRef = ContainerRef(Indirection(evalStr='base.__dict__'))
self._nameContainer(base.__dict__, ContainerRef.BaseRef) self._nameContainer(base.__dict__, self._baseObjRef)
try: try:
simbase simbase
except: except:
pass pass
else: else:
ContainerRef.BaseRef = ContainerRef(indirection=Indirection(evalStr='simbase.__dict__')) self._baseObjRef = ContainerRef(Indirection(evalStr='simbase.__dict__'))
self._nameContainer(simbase.__dict__, ContainerRef.BaseRef) self._nameContainer(simbase.__dict__, self._baseObjRef)
if config.GetBool('leak-container', 0):
_createContainerLeak()
self._curObjRef = self._baseObjRef
self._curObjRef = ContainerRef()
jobMgr.add(self) jobMgr.add(self)
ContainerLeakDetector.PrivateIds.update(set([ ContainerLeakDetector.PrivateIds.update(set([
id(ContainerLeakDetector.PrivateIds), id(ContainerLeakDetector.PrivateIds),
id(self._id2ref), id(self.__dict__),
])) ]))
def destroy(self): def destroy(self):
@ -260,14 +375,23 @@ class ContainerLeakDetector(Job):
jobMgr.remove(self._checkContainersJob) jobMgr.remove(self._checkContainersJob)
self._checkContainersJob = None self._checkContainersJob = None
del self._id2ref del self._id2ref
del self._index2containerName2len del self._index2containerId2len
del self._index2delay del self._index2delay
def getPriority(self): def getPriority(self):
return self._priority return self._priority
def getCheckTaskName(self): @classmethod
def addPrivateId(cls, obj):
cls.PrivateIds.add(id(obj))
@classmethod
def removePrivateId(cls, obj):
cls.PrivateIds.remove(id(obj))
def _getCheckTaskName(self):
return 'checkForLeakingContainers-%s' % self._serialNum return 'checkForLeakingContainers-%s' % self._serialNum
def _getPruneTaskName(self):
return 'pruneLeakingContainerRefs-%s' % self._serialNum
def getLeakEvent(self): def getLeakEvent(self):
# passes description string as argument # passes description string as argument
@ -285,7 +409,9 @@ class ContainerLeakDetector(Job):
def run(self): def run(self):
taskMgr.doMethodLater(self._nextCheckDelay, self._checkForLeaks, taskMgr.doMethodLater(self._nextCheckDelay, self._checkForLeaks,
self.getCheckTaskName()) self._getCheckTaskName())
taskMgr.doMethodLater(self._pruneTaskPeriod, self._pruneContainerRefs,
self._getPruneTaskName())
while True: while True:
# yield up here instead of at the end, since we skip back to the # yield up here instead of at the end, since we skip back to the
@ -294,7 +420,7 @@ class ContainerLeakDetector(Job):
#import pdb;pdb.set_trace() #import pdb;pdb.set_trace()
curObj = None curObj = None
if self._curObjRef is None: if self._curObjRef is None:
self._curObjRef = random.choice(self._id2ref.values()) self._curObjRef = self._baseObjRef
try: try:
curObj = self._curObjRef.getContainer() curObj = self._curObjRef.getContainer()
except: except:
@ -308,26 +434,17 @@ class ContainerLeakDetector(Job):
del self._id2ref[_id] del self._id2ref[_id]
self._curObjRef = self._id2ref[_id] self._curObjRef = self._id2ref[_id]
#print '%s: %s, %s' % (id(curObj), type(curObj), self._id2ref[id(curObj)]) #print '%s: %s, %s' % (id(curObj), type(curObj), self._id2ref[id(curObj)])
self.notify.debug('--> %s' % self._curObjRef) #self.notify.debug('--> %s' % self._curObjRef)
# keep a copy of this obj's eval str, it might not be in _id2ref # keep a copy of this obj's eval str, it might not be in _id2ref
curObjRef = self._curObjRef curObjRef = self._curObjRef
# if we hit a dead end, start over at a container we know about # if we hit a dead end, start over at a container we know about
self._curObjRef = None self._curObjRef = None
try:
if curObj.__class__.__name__ == 'method-wrapper':
continue
except:
pass
if type(curObj) in (types.StringType, types.UnicodeType):
continue
if type(curObj) in (types.ModuleType, types.InstanceType): if type(curObj) in (types.ModuleType, types.InstanceType):
child = curObj.__dict__ child = curObj.__dict__
if not self._isDeadEnd(child): if not self._isDeadEnd(child):
self._curObjRef = ContainerRef(curObjRef, indirection=Indirection(evalStr='.__dict__')) self._curObjRef = ContainerRef(Indirection(evalStr='.__dict__'), curObjRef)
if self._isContainer(child): if self._isContainer(child):
self._nameContainer(child, self._curObjRef) self._nameContainer(child, self._curObjRef)
continue continue
@ -345,12 +462,13 @@ class ContainerLeakDetector(Job):
except KeyError, e: except KeyError, e:
self.notify.warning('could not index into %s with key %s' % (curObjRef, key)) self.notify.warning('could not index into %s with key %s' % (curObjRef, key))
continue continue
if not self._isDeadEnd(attr): if not self._isDeadEnd(attr, key):
if curObj is __builtin__.__dict__: if curObj is __builtin__.__dict__:
indirection=Indirection(evalStr=key) indirection=Indirection(evalStr=key)
self._curObjRef = ContainerRef(indirection)
else: else:
indirection=Indirection(dictKey=key) indirection=Indirection(dictKey=key)
self._curObjRef = ContainerRef(curObjRef, indirection=indirection) self._curObjRef = ContainerRef(indirection, curObjRef)
if self._isContainer(attr): if self._isContainer(attr):
self._nameContainer(attr, self._curObjRef) self._nameContainer(attr, self._curObjRef)
del key del key
@ -379,7 +497,7 @@ class ContainerLeakDetector(Job):
random.shuffle(attrs) random.shuffle(attrs)
for attr in attrs: for attr in attrs:
if not self._isDeadEnd(attr): if not self._isDeadEnd(attr):
self._curObjRef = ContainerRef(curObjRef, indirection=Indirection(evalStr='[%s]' % index)) self._curObjRef = ContainerRef(Indirection(evalStr='[%s]' % index), curObjRef)
if self._isContainer(attr): if self._isContainer(attr):
self._nameContainer(attr, self._curObjRef) self._nameContainer(attr, self._curObjRef)
index += 1 index += 1
@ -401,8 +519,8 @@ class ContainerLeakDetector(Job):
random.shuffle(childNames) random.shuffle(childNames)
for childName in childNames: for childName in childNames:
child = getattr(curObj, childName) child = getattr(curObj, childName)
if not self._isDeadEnd(child): if not self._isDeadEnd(child, childName):
self._curObjRef = ContainerRef(curObjRef, indirection=Indirection(evalStr='.%s' % childName)) self._curObjRef = ContainerRef(Indirection(evalStr='.%s' % childName), curObjRef)
if self._isContainer(child): if self._isContainer(child):
self._nameContainer(child, self._curObjRef) self._nameContainer(child, self._curObjRef)
del childName del childName
@ -411,19 +529,31 @@ class ContainerLeakDetector(Job):
yield Job.Done yield Job.Done
def _isDeadEnd(self, obj): def _isDeadEnd(self, obj, objName=None):
if type(obj) in (types.BooleanType, types.BuiltinFunctionType, if type(obj) in (types.BooleanType, types.BuiltinFunctionType,
types.BuiltinMethodType, types.ComplexType, types.BuiltinMethodType, types.ComplexType,
types.FloatType, types.IntType, types.LongType, types.FloatType, types.IntType, types.LongType,
types.NoneType, types.NotImplementedType, types.NoneType, types.NotImplementedType,
types.TypeType, types.CodeType, types.FunctionType): types.TypeType, types.CodeType, types.FunctionType,
types.StringType, types.UnicodeType):
return True
if id(obj) in self._id2ref:
return True return True
# if it's an internal object, ignore it # if it's an internal object, ignore it
if id(obj) in ContainerLeakDetector.PrivateIds: if id(obj) in ContainerLeakDetector.PrivateIds:
return True return True
if id(obj) in self._id2ref: if objName in ('im_self', 'im_class'):
return True return True
try:
className = obj.__class__.__name__
except:
pass
else:
# prevent infinite recursion in built-in containers related to methods
if className == 'method-wrapper':
return True
return False return False
def _isContainer(self, obj): def _isContainer(self, obj):
try: try:
@ -433,9 +563,11 @@ class ContainerLeakDetector(Job):
return True return True
def _nameContainer(self, cont, objRef): def _nameContainer(self, cont, objRef):
"""
if self.notify.getDebug(): if self.notify.getDebug():
self.notify.debug('_nameContainer: %s' % objRef) self.notify.debug('_nameContainer: %s' % objRef)
#printStack() #printStack()
"""
contId = id(cont) contId = id(cont)
# if this container is new, or the objRef repr is shorter than what we already have, # if this container is new, or the objRef repr is shorter than what we already have,
# put it in the table # put it in the table
@ -443,11 +575,18 @@ class ContainerLeakDetector(Job):
self._id2ref[contId] = objRef self._id2ref[contId] = objRef
def _checkForLeaks(self, task=None): def _checkForLeaks(self, task=None):
self._index2delay[len(self._index2containerName2len)] = self._nextCheckDelay self._index2delay[len(self._index2containerId2len)] = self._nextCheckDelay
self._checkContainersJob = CheckContainers( self._checkContainersJob = CheckContainers(
'%s-checkForLeaks' % self.getJobName(), self, len(self._index2containerName2len)) '%s-checkForLeaks' % self.getJobName(), self, len(self._index2containerId2len))
jobMgr.add(self._checkContainersJob) jobMgr.add(self._checkContainersJob)
self._nextCheckDelay *= 2 self._nextCheckDelay *= 2
taskMgr.doMethodLater(self._nextCheckDelay, self._checkForLeaks, taskMgr.doMethodLater(self._nextCheckDelay, self._checkForLeaks,
self.getCheckTaskName()) self._getCheckTaskName())
return task.done
def _pruneContainerRefs(self, task=None):
taskMgr.doMethodLater(self._pruneTaskPeriod, self._pruneContainerRefs,
self._getPruneTaskName())
return task.done