prevent cycles in the ObjectRefs (i.e. base.loader.base.loader)

This commit is contained in:
Darren Ranalli 2007-04-19 22:24:49 +00:00
parent f619fd68bd
commit f82ac8cfde

View File

@ -129,39 +129,55 @@ class Indirection:
def __repr__(self): def __repr__(self):
return self.getString() return self.getString()
class ContainerRef: class ObjectRef:
""" """
stores a reference to a container in a way that does not prevent garbage stores a reference to a container in a way that does not prevent garbage
collection of the container if possible collection of the container if possible
stored as a series of 'indirections' (obj.foo -> '.foo', dict[key] -> '[key]', etc.) stored as a series of 'indirections' (obj.foo -> '.foo', dict[key] -> '[key]', etc.)
""" """
notify = directNotify.newCategory("ContainerRef") notify = directNotify.newCategory("ObjectRef")
class FailedEval(Exception): class FailedEval(Exception):
pass pass
def __init__(self, indirection, other=None): def __init__(self, indirection, objId, other=None):
self._indirections = [] self._indirections = []
# this is a cache of the ids of our component objects
self._objIds = set()
# are we building off of an existing ref? # are we building off of an existing ref?
if other is not None: if other is not None:
self._objIds = set(other._objIds)
for ind in other._indirections: for ind in other._indirections:
self.addIndirection(ind) self._indirections.append(ind)
self.addIndirection(indirection) self._indirections.append(indirection)
# make sure we're not storing a reference to the actual object,
# that could cause a memory leak
assert type(objId) in (types.IntType, types.LongType)
# prevent cycles (i.e. base.loader.base.loader)
assert objId not in self._objIds
self._objIds.add(objId)
# make sure our indirections don't get destroyed while we're using them
for ind in self._indirections:
ind.acquire()
self.notify.debug(repr(self)) self.notify.debug(repr(self))
def destroy(self): def destroy(self):
# re-entrant
for indirection in self._indirections: for indirection in self._indirections:
indirection.release() indirection.release()
self._indirections = [] del self._indirections
def addIndirection(self, indirection):
indirection.acquire()
self._indirections.append(indirection)
def getNumIndirections(self): def getNumIndirections(self):
return len(self._indirections) return len(self._indirections)
def goesThrough(self, obj):
# since we cache the ids involved in this reference,
# this isn't perfect, for example if base.myObject is reassigned
# to a different object after this Ref was created this would return
# false, allowing a ref to base.myObject.otherObject.myObject
return id(obj) in self._objIds
def _getContainerByEval(self, evalStr, curObj=None): def _getContainerByEval(self, evalStr, curObj=None):
if curObj is not None: if curObj is not None:
# eval('curObj.foo.bar.someDict') # eval('curObj.foo.bar.someDict')
@ -263,7 +279,7 @@ class FindContainers(Job):
ContainerLeakDetector.addPrivateObj(self.__dict__) ContainerLeakDetector.addPrivateObj(self.__dict__)
# set up the base containers, the ones that hold most objects # set up the base containers, the ones that hold most objects
ref = ContainerRef(Indirection(evalStr='__builtin__.__dict__')) ref = ObjectRef(Indirection(evalStr='__builtin__.__dict__'), id(__builtin__.__dict__))
self._id2baseStartRef[id(__builtin__.__dict__)] = ref self._id2baseStartRef[id(__builtin__.__dict__)] = ref
# container for objects that want to make sure they are found by # container for objects that want to make sure they are found by
# the object exploration algorithm, including objects that exist # the object exploration algorithm, including objects that exist
@ -271,7 +287,7 @@ class FindContainers(Job):
# framerate, etc. See LeakDetectors.py # framerate, etc. See LeakDetectors.py
if not hasattr(__builtin__, "leakDetectors"): if not hasattr(__builtin__, "leakDetectors"):
__builtin__.leakDetectors = {} __builtin__.leakDetectors = {}
ref = ContainerRef(Indirection(evalStr='leakDetectors')) ref = ObjectRef(Indirection(evalStr='leakDetectors'), id(leakDetectors))
self._id2baseStartRef[id(leakDetectors)] = ref self._id2baseStartRef[id(leakDetectors)] = ref
for i in self._addContainerGen(__builtin__.__dict__, ref): for i in self._addContainerGen(__builtin__.__dict__, ref):
pass pass
@ -280,7 +296,7 @@ class FindContainers(Job):
except: except:
pass pass
else: else:
ref = ContainerRef(Indirection(evalStr='base.__dict__')) ref = ObjectRef(Indirection(evalStr='base.__dict__'), id(base.__dict__))
self._id2baseStartRef[id(base.__dict__)] = ref self._id2baseStartRef[id(base.__dict__)] = ref
for i in self._addContainerGen(base.__dict__, ref): for i in self._addContainerGen(base.__dict__, ref):
pass pass
@ -289,7 +305,7 @@ class FindContainers(Job):
except: except:
pass pass
else: else:
ref = ContainerRef(Indirection(evalStr='simbase.__dict__')) ref = ObjectRef(Indirection(evalStr='simbase.__dict__'), id(simbase.__dict__))
self._id2baseStartRef[id(simbase.__dict__)] = ref self._id2baseStartRef[id(simbase.__dict__)] = ref
for i in self._addContainerGen(simbase.__dict__, ref): for i in self._addContainerGen(simbase.__dict__, ref):
pass pass
@ -459,14 +475,17 @@ class FindContainers(Job):
hasLength = self._hasLength(child) hasLength = self._hasLength(child)
notDeadEnd = not self._isDeadEnd(child) notDeadEnd = not self._isDeadEnd(child)
if hasLength or notDeadEnd: if hasLength or notDeadEnd:
objRef = ContainerRef(Indirection(evalStr='.__dict__'), parentObjRef) # prevent cycles in the references (i.e. base.loader.base)
yield None if not parentObjRef.goesThrough(child):
if hasLength: objRef = ObjectRef(Indirection(evalStr='.__dict__'),
for i in self._addContainerGen(child, objRef): id(child), parentObjRef)
yield None yield None
if notDeadEnd: if hasLength:
self._addDiscoveredStartRef(child, objRef) for i in self._addContainerGen(child, objRef):
curObjRef = objRef yield None
if notDeadEnd:
self._addDiscoveredStartRef(child, objRef)
curObjRef = objRef
continue continue
if type(curObj) is types.DictType: if type(curObj) is types.DictType:
@ -492,18 +511,22 @@ class FindContainers(Job):
if curObjRef is None: if curObjRef is None:
notDeadEnd = not self._isDeadEnd(attr, key) notDeadEnd = not self._isDeadEnd(attr, key)
if hasLength or notDeadEnd: if hasLength or notDeadEnd:
if curObj is __builtin__.__dict__: # prevent cycles in the references (i.e. base.loader.base)
objRef = ContainerRef(Indirection(evalStr='%s' % key)) if not parentObjRef.goesThrough(curObj[key]):
else: if curObj is __builtin__.__dict__:
objRef = ContainerRef(Indirection(dictKey=key), parentObjRef) objRef = ObjectRef(Indirection(evalStr='%s' % key),
yield None id(curObj[key]))
if hasLength: else:
for i in self._addContainerGen(attr, objRef): objRef = ObjectRef(Indirection(dictKey=key),
yield None id(curObj[key]), parentObjRef)
if notDeadEnd: yield None
self._addDiscoveredStartRef(attr, objRef) if hasLength:
if curObjRef is None and random.randrange(numKeysLeft) == 0: for i in self._addContainerGen(attr, objRef):
curObjRef = objRef yield None
if notDeadEnd:
self._addDiscoveredStartRef(attr, objRef)
if curObjRef is None and random.randrange(numKeysLeft) == 0:
curObjRef = objRef
del key del key
del attr del attr
continue continue
@ -537,16 +560,18 @@ class FindContainers(Job):
if curObjRef is None: if curObjRef is None:
notDeadEnd = not self._isDeadEnd(attr) notDeadEnd = not self._isDeadEnd(attr)
if hasLength or notDeadEnd: if hasLength or notDeadEnd:
objRef = ContainerRef(Indirection(evalStr='[%s]' % index), # prevent cycles in the references (i.e. base.loader.base)
parentObjRef) if not parentObjRef.goesThrough(curObj[index]):
yield None objRef = ObjectRef(Indirection(evalStr='[%s]' % index),
if hasLength: id(curObj[index]), parentObjRef)
for i in self._addContainerGen(attr, objRef): yield None
yield None if hasLength:
if notDeadEnd: for i in self._addContainerGen(attr, objRef):
self._addDiscoveredStartRef(attr, objRef) yield None
if curObjRef is None and random.randrange(numAttrsLeft) == 0: if notDeadEnd:
curObjRef = objRef self._addDiscoveredStartRef(attr, objRef)
if curObjRef is None and random.randrange(numAttrsLeft) == 0:
curObjRef = objRef
del attr del attr
except StopIteration, e: except StopIteration, e:
pass pass
@ -698,7 +723,7 @@ class CheckContainers(Job):
raise raise
yield Job.Done yield Job.Done
class PruneContainerRefs(Job): class PruneObjectRefs(Job):
""" """
Job to destroy any container refs that are no longer valid. Job to destroy any container refs that are no longer valid.
Checks validity by asking for each container Checks validity by asking for each container
@ -748,7 +773,7 @@ class PruneContainerRefs(Job):
# reference is invalid, remove it # reference is invalid, remove it
del _id2discoveredStartRef[id] del _id2discoveredStartRef[id]
except Exception, e: except Exception, e:
print 'PruneContainerRefs job caught exception: %s' % e print 'PruneObjectRefs job caught exception: %s' % e
if __dev__: if __dev__:
raise raise
yield Job.Done yield Job.Done
@ -883,12 +908,12 @@ class ContainerLeakDetector(Job):
return task.done return task.done
def _scheduleNextPruning(self): def _scheduleNextPruning(self):
taskMgr.doMethodLater(self._pruneTaskPeriod, self._pruneContainerRefs, taskMgr.doMethodLater(self._pruneTaskPeriod, self._pruneObjectRefs,
self._getPruneTaskName()) self._getPruneTaskName())
def _pruneContainerRefs(self, task=None): def _pruneObjectRefs(self, task=None):
self._pruneContainersJob = PruneContainerRefs( self._pruneContainersJob = PruneObjectRefs(
'%s-pruneContainerRefs' % self.getJobName(), self) '%s-pruneObjectRefs' % self.getJobName(), self)
self.acceptOnce(self._pruneContainersJob.getFinishedEvent(), self.acceptOnce(self._pruneContainersJob.getFinishedEvent(),
self._scheduleNextPruning) self._scheduleNextPruning)
jobMgr.add(self._pruneContainersJob) jobMgr.add(self._pruneContainersJob)