mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 19:08:55 -04:00
fixed Long-Int leak in doId location tracking
This commit is contained in:
parent
7ebfa95b92
commit
9ce7a90a38
@ -219,8 +219,8 @@ class DistributedObject(DistributedObjectBase):
|
|||||||
self.activeState = ESDisabled
|
self.activeState = ESDisabled
|
||||||
self.__callbacks = {}
|
self.__callbacks = {}
|
||||||
self.cr.closeAutoInterests(self)
|
self.cr.closeAutoInterests(self)
|
||||||
#self.cr.deleteObjectLocation(self.doId, self.parentId, self.zoneId)
|
|
||||||
self.setLocation(0,0)
|
self.setLocation(0,0)
|
||||||
|
self.cr.deleteObjectLocation(self.doId, self.parentId, self.zoneId)
|
||||||
# TODO: disable my children
|
# TODO: disable my children
|
||||||
|
|
||||||
def isDisabled(self):
|
def isDisabled(self):
|
||||||
|
@ -335,8 +335,6 @@ class DistributedObjectAI(DistributedObjectBase):
|
|||||||
|
|
||||||
# The repository is the one that really does the work
|
# The repository is the one that really does the work
|
||||||
parentId = self.air.districtId
|
parentId = self.air.districtId
|
||||||
self.parentId = parentId
|
|
||||||
self.zoneId = zoneId
|
|
||||||
self.air.generateWithRequired(self, parentId, zoneId, optionalFields)
|
self.air.generateWithRequired(self, parentId, zoneId, optionalFields)
|
||||||
self.generate()
|
self.generate()
|
||||||
self.announceGenerate()
|
self.announceGenerate()
|
||||||
@ -352,8 +350,6 @@ class DistributedObjectAI(DistributedObjectBase):
|
|||||||
|
|
||||||
# The repository is the one that really does the work
|
# The repository is the one that really does the work
|
||||||
self.air.generateWithRequiredAndId(self, doId, parentId, zoneId, optionalFields)
|
self.air.generateWithRequiredAndId(self, doId, parentId, zoneId, optionalFields)
|
||||||
self.parentId = parentId
|
|
||||||
self.zoneId = zoneId
|
|
||||||
self.generate()
|
self.generate()
|
||||||
self.announceGenerate()
|
self.announceGenerate()
|
||||||
|
|
||||||
@ -374,10 +370,6 @@ class DistributedObjectAI(DistributedObjectBase):
|
|||||||
self.air.addDOToTables(self, location=(parentId, zoneId))
|
self.air.addDOToTables(self, location=(parentId, zoneId))
|
||||||
# Send a generate message
|
# Send a generate message
|
||||||
self.sendGenerateWithRequired(self.air, parentId, zoneId, optionalFields)
|
self.sendGenerateWithRequired(self.air, parentId, zoneId, optionalFields)
|
||||||
|
|
||||||
## assert not hasattr(self, 'parentId') or self.parentId is None
|
|
||||||
## self.parentId = parentId
|
|
||||||
## self.zoneId = zoneId
|
|
||||||
self.generate()
|
self.generate()
|
||||||
self.announceGenerate()
|
self.announceGenerate()
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from direct.distributed import DoHierarchy
|
||||||
|
|
||||||
#hack:
|
#hack:
|
||||||
BAD_DO_ID = BAD_ZONE_ID = 0 # 0xFFFFFFFF
|
BAD_DO_ID = BAD_ZONE_ID = 0 # 0xFFFFFFFF
|
||||||
BAD_CHANNEL_ID = 0 # 0xFFFFFFFFFFFFFFFF
|
BAD_CHANNEL_ID = 0 # 0xFFFFFFFFFFFFFFFF
|
||||||
@ -15,7 +17,7 @@ class DoCollectionManager:
|
|||||||
# Dict of {
|
# Dict of {
|
||||||
# parent DistributedObject id:
|
# parent DistributedObject id:
|
||||||
# { zoneIds: [child DistributedObject ids] }}
|
# { zoneIds: [child DistributedObject ids] }}
|
||||||
self.__doHierarchy = {}
|
self._doHierarchy = DoHierarchy.DoHierarchy()
|
||||||
|
|
||||||
def getDo(self, doId):
|
def getDo(self, doId):
|
||||||
return self.doId2do.get(doId)
|
return self.doId2do.get(doId)
|
||||||
@ -64,9 +66,6 @@ class DoCollectionManager:
|
|||||||
matches.append(value)
|
matches.append(value)
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
def getDoHierarchy(self):
|
|
||||||
return self.__doHierarchy
|
|
||||||
|
|
||||||
if __debug__:
|
if __debug__:
|
||||||
def printObjects(self):
|
def printObjects(self):
|
||||||
format="%10s %10s %10s %30s %20s"
|
format="%10s %10s %10s %30s %20s"
|
||||||
@ -98,36 +97,7 @@ class DoCollectionManager:
|
|||||||
for i in self.getDoIdList(parentId, zoneId, classType)]
|
for i in self.getDoIdList(parentId, zoneId, classType)]
|
||||||
|
|
||||||
def getDoIdList(self, parentId, zoneId=None, classType=None):
|
def getDoIdList(self, parentId, zoneId=None, classType=None):
|
||||||
"""
|
return self._doHierarchy.getDoIds(parentId, zoneId, classType)
|
||||||
parentId is any distributed object id.
|
|
||||||
zoneId is a uint32, defaults to None (all zones). Try zone 2 if
|
|
||||||
you're not sure which zone to use (0 is a bad/null zone and
|
|
||||||
1 has had reserved use in the past as a no messages zone, while
|
|
||||||
2 has traditionally been a global, uber, misc stuff zone).
|
|
||||||
dclassType is a distributed class type filter, defaults
|
|
||||||
to None (no filter).
|
|
||||||
|
|
||||||
If dclassName is None then all objects in the zone are returned;
|
|
||||||
otherwise the list is filtered to only include objects of that type.
|
|
||||||
"""
|
|
||||||
parent=self.__doHierarchy.get(parentId)
|
|
||||||
if parent is None:
|
|
||||||
return []
|
|
||||||
if zoneId is None:
|
|
||||||
r = []
|
|
||||||
for zone in parent.values():
|
|
||||||
for obj in zone:
|
|
||||||
r.append(obj)
|
|
||||||
else:
|
|
||||||
r = parent.get(zoneId, [])
|
|
||||||
if classType is not None:
|
|
||||||
a = []
|
|
||||||
for doId in r:
|
|
||||||
obj = self.getDo(doId)
|
|
||||||
if isinstance(obj, classType):
|
|
||||||
a.append(doId)
|
|
||||||
r = a
|
|
||||||
return r
|
|
||||||
|
|
||||||
def getOwnerViewDoList(self, classType):
|
def getOwnerViewDoList(self, classType):
|
||||||
assert self.hasOwnerView()
|
assert self.hasOwnerView()
|
||||||
@ -186,10 +156,10 @@ class DoCollectionManager:
|
|||||||
self.deleteObjects()
|
self.deleteObjects()
|
||||||
|
|
||||||
# the zoneId2doIds table should be empty now
|
# the zoneId2doIds table should be empty now
|
||||||
if len(self.__doHierarchy) > 0:
|
if not self._doHierarchy.isEmpty():
|
||||||
self.notify.warning(
|
self.notify.warning(
|
||||||
'__doHierarchy table not empty: %s' % self.__doHierarchy)
|
'_doHierarchy table not empty: %s' % self._doHierarchy)
|
||||||
self.__doHierarchy = {}
|
self._doHierarchy.clear()
|
||||||
|
|
||||||
def handleObjectLocation(self, di):
|
def handleObjectLocation(self, di):
|
||||||
# CLIENT_OBJECT_LOCATION
|
# CLIENT_OBJECT_LOCATION
|
||||||
@ -203,7 +173,7 @@ class DoCollectionManager:
|
|||||||
(doId, parentId, zoneId))
|
(doId, parentId, zoneId))
|
||||||
# Let the object finish the job
|
# Let the object finish the job
|
||||||
obj.setLocation(parentId, zoneId)
|
obj.setLocation(parentId, zoneId)
|
||||||
self.storeObjectLocation(doId, parentId, zoneId)
|
#self.storeObjectLocation(doId, parentId, zoneId)
|
||||||
else:
|
else:
|
||||||
self.notify.warning(
|
self.notify.warning(
|
||||||
"handleObjectLocation: Asked to update non-existent obj: %s" % (doId))
|
"handleObjectLocation: Asked to update non-existent obj: %s" % (doId))
|
||||||
@ -222,31 +192,31 @@ class DoCollectionManager:
|
|||||||
|
|
||||||
def storeObjectLocation(self, doId, parentId, zoneId, object=None):
|
def storeObjectLocation(self, doId, parentId, zoneId, object=None):
|
||||||
if (object == None):
|
if (object == None):
|
||||||
obj = self.doId2do.get(doId)
|
object = self.doId2do.get(doId)
|
||||||
|
if object is None:
|
||||||
|
self.notify.error('storeObjectLocation: object %s not present' % doId)
|
||||||
else:
|
else:
|
||||||
obj = object
|
oldParentId = object.parentId
|
||||||
if obj is None:
|
oldZoneId = object.zoneId
|
||||||
self.notify.warning('storeObjectLocation: object %s not present' % doId)
|
if ((None not in (oldParentId, oldZoneId)) and
|
||||||
else:
|
((oldParentId != parentId) or (oldZoneId != zoneId))):
|
||||||
oldParentId = obj.parentId
|
|
||||||
oldZoneId = obj.zoneId
|
|
||||||
if oldParentId != parentId:
|
|
||||||
# Remove old location
|
# Remove old location
|
||||||
self.deleteObjectLocation(doId, oldParentId, oldZoneId)
|
self.deleteObjectLocation(doId, oldParentId, oldZoneId)
|
||||||
if oldParentId == parentId and oldZoneId == zoneId:
|
elif oldParentId == parentId and oldZoneId == zoneId:
|
||||||
# object is already at that parent and zone
|
# object is already at that parent and zone
|
||||||
return
|
return
|
||||||
if (parentId is None) or (zoneId is None):
|
if (parentId is None) or (zoneId is None):
|
||||||
# Do not store null values
|
# Do not store null values
|
||||||
return
|
return
|
||||||
# Add to new location
|
# Add to new location
|
||||||
parentZoneDict = self.__doHierarchy.setdefault(parentId, {})
|
self._doHierarchy.storeObjectLocation(doId, parentId, zoneId)
|
||||||
zoneDoSet = parentZoneDict.setdefault(zoneId, set())
|
# this check doesn't work because of global UD objects;
|
||||||
zoneDoSet.add(doId)
|
# should they have a location?
|
||||||
|
#assert len(self._doHierarchy) == len(self.doId2do)
|
||||||
|
|
||||||
# Set the new parent and zone on the object
|
# Set the new parent and zone on the object
|
||||||
obj.parentId = parentId
|
object.parentId = parentId
|
||||||
obj.zoneId = zoneId
|
object.zoneId = zoneId
|
||||||
|
|
||||||
if 1:
|
if 1:
|
||||||
# Do we still need this
|
# Do we still need this
|
||||||
@ -256,7 +226,7 @@ class DoCollectionManager:
|
|||||||
# scene graph reparent the child to some subnode it owns.
|
# scene graph reparent the child to some subnode it owns.
|
||||||
parentObj = self.doId2do.get(parentId)
|
parentObj = self.doId2do.get(parentId)
|
||||||
if parentObj is not None:
|
if parentObj is not None:
|
||||||
parentObj.handleChildArrive(obj, zoneId)
|
parentObj.handleChildArrive(object, zoneId)
|
||||||
elif parentId not in (0, self.getGameDoId()):
|
elif parentId not in (0, self.getGameDoId()):
|
||||||
self.notify.warning('storeObjectLocation(%s): parent %s not present' %
|
self.notify.warning('storeObjectLocation(%s): parent %s not present' %
|
||||||
(doId, parentId))
|
(doId, parentId))
|
||||||
@ -274,23 +244,7 @@ class DoCollectionManager:
|
|||||||
if oldParentObj is not None and obj is not None:
|
if oldParentObj is not None and obj is not None:
|
||||||
oldParentObj.handleChildLeave(obj, zoneId)
|
oldParentObj.handleChildLeave(obj, zoneId)
|
||||||
|
|
||||||
parentZoneDict = self.__doHierarchy.get(parentId)
|
self._doHierarchy.deleteObjectLocation(doId, parentId, zoneId)
|
||||||
if parentZoneDict is not None:
|
|
||||||
zoneDoSet = parentZoneDict.get(zoneId)
|
|
||||||
if zoneDoSet is not None:
|
|
||||||
if doId in zoneDoSet:
|
|
||||||
zoneDoSet.remove(doId)
|
|
||||||
if len(zoneDoSet) == 0:
|
|
||||||
del parentZoneDict[zoneId]
|
|
||||||
else:
|
|
||||||
self.notify.warning(
|
|
||||||
"deleteObjectLocation: objId: %s not found"%(doId,))
|
|
||||||
else:
|
|
||||||
self.notify.warning(
|
|
||||||
"deleteObjectLocation: zoneId: %s not found"%(zoneId,))
|
|
||||||
else:
|
|
||||||
self.notify.warning(
|
|
||||||
"deleteObjectLocation: parentId: %s not found"%(parentId,))
|
|
||||||
|
|
||||||
def addDOToTables(self, do, location=None, ownerView=False):
|
def addDOToTables(self, do, location=None, ownerView=False):
|
||||||
assert self.notify.debugStateCall(self)
|
assert self.notify.debugStateCall(self)
|
||||||
@ -328,15 +282,7 @@ class DoCollectionManager:
|
|||||||
if __debug__:
|
if __debug__:
|
||||||
def isInDoTables(self, doId):
|
def isInDoTables(self, doId):
|
||||||
assert self.notify.debugStateCall(self)
|
assert self.notify.debugStateCall(self)
|
||||||
inDoHierarchy = False
|
return doId in self.doId2do
|
||||||
for parentId, parentZoneDict in self.__doHierarchy.items():
|
|
||||||
for zoneId, zoneDoSet in parentZoneDict.items():
|
|
||||||
if doId in zoneDoSet:
|
|
||||||
inDoHierarchy = True
|
|
||||||
print "isInDoTables found " \
|
|
||||||
"%s in parentId:%s zoneId:%s"%(
|
|
||||||
doId, parentId, zoneId)
|
|
||||||
return inDoHierarchy or (doId in self.doId2do)
|
|
||||||
|
|
||||||
def removeDOFromTables(self, do):
|
def removeDOFromTables(self, do):
|
||||||
assert self.notify.debugStateCall(self)
|
assert self.notify.debugStateCall(self)
|
||||||
|
91
direct/src/distributed/DoHierarchy.py
Executable file
91
direct/src/distributed/DoHierarchy.py
Executable file
@ -0,0 +1,91 @@
|
|||||||
|
from direct.directnotify.DirectNotifyGlobal import directNotify
|
||||||
|
|
||||||
|
class DoHierarchy:
|
||||||
|
"""
|
||||||
|
This table has been a source of memory leaks, with DoIds getting left in the table indefinitely.
|
||||||
|
DoHierarchy guards access to the table and ensures correctness.
|
||||||
|
"""
|
||||||
|
notify = directNotify.newCategory("DoHierarchy")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# parentId->zoneId->set(child DoIds)
|
||||||
|
self._table = {}
|
||||||
|
self._allDoIds = set()
|
||||||
|
|
||||||
|
def isEmpty(self):
|
||||||
|
assert ((len(self._table) == 0) == (len(self._allDoIds) == 0))
|
||||||
|
return len(self._table) == 0 and len(self._allDoIds) == 0
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._allDoIds)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
assert self.notify.debugCall()
|
||||||
|
self._table = {}
|
||||||
|
self._allDoIds = set()
|
||||||
|
|
||||||
|
def getDoIds(self, parentId, zoneId=None, classType=None):
|
||||||
|
"""
|
||||||
|
Moved from DoCollectionManager
|
||||||
|
==============================
|
||||||
|
parentId is any distributed object id.
|
||||||
|
zoneId is a uint32, defaults to None (all zones). Try zone 2 if
|
||||||
|
you're not sure which zone to use (0 is a bad/null zone and
|
||||||
|
1 has had reserved use in the past as a no messages zone, while
|
||||||
|
2 has traditionally been a global, uber, misc stuff zone).
|
||||||
|
dclassType is a distributed class type filter, defaults
|
||||||
|
to None (no filter).
|
||||||
|
|
||||||
|
If dclassName is None then all objects in the zone are returned;
|
||||||
|
otherwise the list is filtered to only include objects of that type.
|
||||||
|
"""
|
||||||
|
parent=self._table.get(parentId)
|
||||||
|
if parent is None:
|
||||||
|
return []
|
||||||
|
if zoneId is None:
|
||||||
|
r = []
|
||||||
|
for zone in parent.values():
|
||||||
|
for obj in zone:
|
||||||
|
r.append(obj)
|
||||||
|
else:
|
||||||
|
r = parent.get(zoneId, [])
|
||||||
|
if classType is not None:
|
||||||
|
a = []
|
||||||
|
for doId in r:
|
||||||
|
obj = self.getDo(doId)
|
||||||
|
if isinstance(obj, classType):
|
||||||
|
a.append(doId)
|
||||||
|
r = a
|
||||||
|
return r
|
||||||
|
|
||||||
|
def storeObjectLocation(self, doId, parentId, zoneId):
|
||||||
|
assert self.notify.debugCall()
|
||||||
|
assert doId not in self._allDoIds
|
||||||
|
parentZoneDict = self._table.setdefault(parentId, {})
|
||||||
|
zoneDoSet = parentZoneDict.setdefault(zoneId, set())
|
||||||
|
zoneDoSet.add(doId)
|
||||||
|
self._allDoIds.add(doId)
|
||||||
|
|
||||||
|
def deleteObjectLocation(self, doId, parentId, zoneId):
|
||||||
|
assert self.notify.debugCall()
|
||||||
|
assert doId in self._allDoIds
|
||||||
|
parentZoneDict = self._table.get(parentId)
|
||||||
|
if parentZoneDict is not None:
|
||||||
|
zoneDoSet = parentZoneDict.get(zoneId)
|
||||||
|
if zoneDoSet is not None:
|
||||||
|
if doId in zoneDoSet:
|
||||||
|
zoneDoSet.remove(doId)
|
||||||
|
self._allDoIds.remove(doId)
|
||||||
|
if len(zoneDoSet) == 0:
|
||||||
|
del parentZoneDict[zoneId]
|
||||||
|
if len(parentZoneDict) == 0:
|
||||||
|
del self._table[parentId]
|
||||||
|
else:
|
||||||
|
self.notify.error(
|
||||||
|
"deleteObjectLocation: objId: %s not found" % doId)
|
||||||
|
else:
|
||||||
|
self.notify.error(
|
||||||
|
"deleteObjectLocation: zoneId: %s not found" % zoneId)
|
||||||
|
else:
|
||||||
|
self.notify.error(
|
||||||
|
"deleteObjectLocation: parentId: %s not found" % parentId)
|
Loading…
x
Reference in New Issue
Block a user