mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-02 09:52:27 -04:00
added CRDataCache
This commit is contained in:
parent
91147411ee
commit
ef0ba78f1d
113
direct/src/distributed/CRDataCache.py
Executable file
113
direct/src/distributed/CRDataCache.py
Executable file
@ -0,0 +1,113 @@
|
|||||||
|
from direct.distributed.CachedDOData import CachedDOData
|
||||||
|
|
||||||
|
class CRDataCache:
|
||||||
|
# Stores cached data for DistributedObjects between instantiations on the client
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._doId2name2data = {}
|
||||||
|
# maximum # of objects we will cache data for
|
||||||
|
self._size = config.GetInt('crdatacache-size', 10)
|
||||||
|
assert self._size > 0
|
||||||
|
# used to preserve the cache size
|
||||||
|
self._junkIndex = 0
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
del self._doId2name2data
|
||||||
|
|
||||||
|
def setCachedData(self, doId, name, data):
|
||||||
|
# stores a set of named data for a DistributedObject
|
||||||
|
assert isinstance(data, CachedDOData)
|
||||||
|
if len(self._doId2name2data) >= self._size:
|
||||||
|
# cache is full, throw out a random doId's data
|
||||||
|
if self._junkIndex >= len(self._doId2name2data):
|
||||||
|
self._junkIndex = 0
|
||||||
|
junkDoId = self._doId2name2data.keys()[self._junkIndex]
|
||||||
|
self._junkIndex += 1
|
||||||
|
for name in self._doId2name2data[junkDoId]:
|
||||||
|
self._doId2name2data[junkDoId][name].flush()
|
||||||
|
del self._doId2name2data[junkDoId]
|
||||||
|
|
||||||
|
self._doId2name2data.setdefault(doId, {})
|
||||||
|
cachedData = self._doId2name2data[doId].get(name)
|
||||||
|
if cachedData:
|
||||||
|
cachedData.flush()
|
||||||
|
cachedData.destroy()
|
||||||
|
self._doId2name2data[doId][name] = data
|
||||||
|
|
||||||
|
def hasCachedData(self, doId):
|
||||||
|
return doId in self._doId2name2data
|
||||||
|
|
||||||
|
def popCachedData(self, doId):
|
||||||
|
# retrieves all cached data for a DistributedObject and removes it from the cache
|
||||||
|
data = self._doId2name2data[doId]
|
||||||
|
del self._doId2name2data[doId]
|
||||||
|
return data
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
# get rid of all cached data
|
||||||
|
for doId in self._doId2name2data:
|
||||||
|
for name in self._doId2name2data[doId]:
|
||||||
|
self._doId2name2data[doId][name].flush()
|
||||||
|
self._doId2name2data = {}
|
||||||
|
|
||||||
|
if __debug__:
|
||||||
|
def _startMemLeakCheck(self):
|
||||||
|
self._len = len(self._doId2name2data)
|
||||||
|
|
||||||
|
def _stopMemLeakCheck(self):
|
||||||
|
del self._len
|
||||||
|
|
||||||
|
def _checkMemLeaks(self):
|
||||||
|
assert self._len == len(self._doId2name2data)
|
||||||
|
|
||||||
|
if __debug__:
|
||||||
|
class TestCachedData(CachedDOData):
|
||||||
|
def __init__(self):
|
||||||
|
CachedDOData.__init__(self)
|
||||||
|
self._destroyed = False
|
||||||
|
self._flushed = False
|
||||||
|
def destroy(self):
|
||||||
|
CachedDOData.destroy(self)
|
||||||
|
self._destroyed = True
|
||||||
|
def flush(self):
|
||||||
|
CachedDOData.flush(self)
|
||||||
|
self._flushed = True
|
||||||
|
|
||||||
|
dc = CRDataCache()
|
||||||
|
dc._startMemLeakCheck()
|
||||||
|
|
||||||
|
cd = CachedDOData()
|
||||||
|
cd.foo = 34
|
||||||
|
dc.setCachedData(1, 'testCachedData', cd)
|
||||||
|
del cd
|
||||||
|
cd = CachedDOData()
|
||||||
|
cd.bar = 45
|
||||||
|
dc.setCachedData(1, 'testCachedData2', cd)
|
||||||
|
del cd
|
||||||
|
assert dc.hasCachedData(1)
|
||||||
|
assert dc.hasCachedData(1)
|
||||||
|
assert not dc.hasCachedData(2)
|
||||||
|
# data is dict of dataName->data
|
||||||
|
data = dc.popCachedData(1)
|
||||||
|
assert len(data) == 2
|
||||||
|
assert 'testCachedData' in data
|
||||||
|
assert 'testCachedData2' in data
|
||||||
|
assert data['testCachedData'].foo == 34
|
||||||
|
assert data['testCachedData2'].bar == 45
|
||||||
|
for cd in data.itervalues():
|
||||||
|
cd.flush()
|
||||||
|
del data
|
||||||
|
dc._checkMemLeaks()
|
||||||
|
|
||||||
|
cd = CachedDOData()
|
||||||
|
cd.bar = 1234
|
||||||
|
dc.setCachedData(43, 'testCachedData2', cd)
|
||||||
|
del cd
|
||||||
|
assert dc.hasCachedData(43)
|
||||||
|
dc.flush()
|
||||||
|
dc._checkMemLeaks()
|
||||||
|
|
||||||
|
dc._stopMemLeakCheck()
|
||||||
|
dc.destroy()
|
||||||
|
del dc
|
||||||
|
|
22
direct/src/distributed/CachedDOData.py
Executable file
22
direct/src/distributed/CachedDOData.py
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
class CachedDOData:
|
||||||
|
# base class for objects that are used to store data in the CRDataCache
|
||||||
|
#
|
||||||
|
# stores a minimal set of cached data for DistributedObjects between instantiations
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# override and store cached data
|
||||||
|
# this object now owns the data
|
||||||
|
# ownership will either pass back to another instantion of the object,
|
||||||
|
# or the data will be flushed
|
||||||
|
pass
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
# override and handle this object being destroyed
|
||||||
|
# this is destruction of this object, not the cached data (see flush)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
# override and destroy the cached data
|
||||||
|
# cached data is typically created by the DistributedObject and destroyed here
|
||||||
|
pass
|
@ -58,6 +58,7 @@ class ClientRepository(ClientRepositoryBase):
|
|||||||
obj.doId = id
|
obj.doId = id
|
||||||
self.doId2do[id] = obj
|
self.doId2do[id] = obj
|
||||||
obj.generateInit()
|
obj.generateInit()
|
||||||
|
obj._retrieveCachedData()
|
||||||
obj.generate()
|
obj.generate()
|
||||||
obj.announceGenerate()
|
obj.announceGenerate()
|
||||||
datagram = dclass.clientFormatGenerate(obj, id, zoneId, optionalFields)
|
datagram = dclass.clientFormatGenerate(obj, id, zoneId, optionalFields)
|
||||||
@ -201,6 +202,7 @@ class ClientRepository(ClientRepositoryBase):
|
|||||||
self.doId2do[doId] = distObj
|
self.doId2do[doId] = distObj
|
||||||
# Update the required fields
|
# Update the required fields
|
||||||
distObj.generateInit() # Only called when constructed
|
distObj.generateInit() # Only called when constructed
|
||||||
|
distObj._retrieveCachedData()
|
||||||
distObj.generate()
|
distObj.generate()
|
||||||
distObj.updateRequiredFields(dclass, di)
|
distObj.updateRequiredFields(dclass, di)
|
||||||
# updateRequiredFields calls announceGenerate
|
# updateRequiredFields calls announceGenerate
|
||||||
|
@ -3,6 +3,7 @@ from MsgTypes import *
|
|||||||
from direct.task import Task
|
from direct.task import Task
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
import CRCache
|
import CRCache
|
||||||
|
from direct.distributed.CRDataCache import CRDataCache
|
||||||
from direct.distributed.ConnectionRepository import ConnectionRepository
|
from direct.distributed.ConnectionRepository import ConnectionRepository
|
||||||
from direct.showbase import PythonUtil
|
from direct.showbase import PythonUtil
|
||||||
import ParentMgr
|
import ParentMgr
|
||||||
@ -44,6 +45,7 @@ class ClientRepositoryBase(ConnectionRepository):
|
|||||||
|
|
||||||
self.readDCFile(dcFileNames)
|
self.readDCFile(dcFileNames)
|
||||||
self.cache=CRCache.CRCache()
|
self.cache=CRCache.CRCache()
|
||||||
|
self.doDataCache = CRDataCache()
|
||||||
self.cacheOwner=CRCache.CRCache()
|
self.cacheOwner=CRCache.CRCache()
|
||||||
self.serverDelta = 0
|
self.serverDelta = 0
|
||||||
|
|
||||||
@ -365,6 +367,7 @@ class ClientRepositoryBase(ConnectionRepository):
|
|||||||
self.doId2do[doId] = distObj
|
self.doId2do[doId] = distObj
|
||||||
# Update the required fields
|
# Update the required fields
|
||||||
distObj.generateInit() # Only called when constructed
|
distObj.generateInit() # Only called when constructed
|
||||||
|
distObj._retrieveCachedData()
|
||||||
distObj.generate()
|
distObj.generate()
|
||||||
distObj.setLocation(parentId, zoneId)
|
distObj.setLocation(parentId, zoneId)
|
||||||
distObj.updateRequiredFields(dclass, di)
|
distObj.updateRequiredFields(dclass, di)
|
||||||
@ -412,6 +415,7 @@ class ClientRepositoryBase(ConnectionRepository):
|
|||||||
self.doId2do[doId] = distObj
|
self.doId2do[doId] = distObj
|
||||||
# Update the required fields
|
# Update the required fields
|
||||||
distObj.generateInit() # Only called when constructed
|
distObj.generateInit() # Only called when constructed
|
||||||
|
distObj._retrieveCachedData()
|
||||||
distObj.generate()
|
distObj.generate()
|
||||||
distObj.setLocation(parentId, zoneId)
|
distObj.setLocation(parentId, zoneId)
|
||||||
distObj.updateRequiredOtherFields(dclass, di)
|
distObj.updateRequiredOtherFields(dclass, di)
|
||||||
|
@ -154,6 +154,35 @@ class DistributedObject(DistributedObjectBase):
|
|||||||
def getNeverDisable(self):
|
def getNeverDisable(self):
|
||||||
return self.neverDisable
|
return self.neverDisable
|
||||||
|
|
||||||
|
def _retrieveCachedData(self):
|
||||||
|
# once we know our doId, grab any data that might be stored in the data cache
|
||||||
|
# from the last time we were on the client
|
||||||
|
if self.cr.doDataCache.hasCachedData(self.doId):
|
||||||
|
self._cachedData = self.cr.doDataCache.popCachedData(self.doId)
|
||||||
|
|
||||||
|
def setCachedData(self, name, data):
|
||||||
|
assert type(name) == type('')
|
||||||
|
# ownership of the data passes to the repository data cache
|
||||||
|
self.cr.doDataCache.setCachedData(self.doId, name, data)
|
||||||
|
|
||||||
|
def hasCachedData(self, name):
|
||||||
|
assert type(name) == type('')
|
||||||
|
if not hasattr(self, '_cachedData'):
|
||||||
|
return False
|
||||||
|
return name in self._cachedData
|
||||||
|
|
||||||
|
def getCachedData(self, name):
|
||||||
|
assert type(name) == type('')
|
||||||
|
# ownership of the data passes to the caller of this method
|
||||||
|
data = self._cachedData[name]
|
||||||
|
del self._cachedData[name]
|
||||||
|
return data
|
||||||
|
|
||||||
|
def flushCachedData(self, name):
|
||||||
|
assert type(name) == type('')
|
||||||
|
# call this to throw out cached data from a previous instantiation
|
||||||
|
self._cachedData[name].flush()
|
||||||
|
|
||||||
def setCacheable(self, bool):
|
def setCacheable(self, bool):
|
||||||
assert bool == 1 or bool == 0
|
assert bool == 1 or bool == 0
|
||||||
self.cacheable = bool
|
self.cacheable = bool
|
||||||
@ -272,6 +301,13 @@ class DistributedObject(DistributedObjectBase):
|
|||||||
# after this is called, the object is no longer a DistributedObject
|
# after this is called, the object is no longer a DistributedObject
|
||||||
# but may still be used as a DelayDeleted object
|
# but may still be used as a DelayDeleted object
|
||||||
self.destroyDoStackTrace = StackTrace()
|
self.destroyDoStackTrace = StackTrace()
|
||||||
|
# check for leftover cached data that was not retrieved or flushed by this object
|
||||||
|
# this will catch typos in the data name in calls to get/setCachedData
|
||||||
|
if hasattr(self, '_cachedData'):
|
||||||
|
for name, cachedData in self._cachedData.iteritems():
|
||||||
|
self.notify.warning('flushing unretrieved cached data: %s' % name)
|
||||||
|
cachedData.flush()
|
||||||
|
del self._cachedData
|
||||||
self.cr = None
|
self.cr = None
|
||||||
self.dclass = None
|
self.dclass = None
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ class OldClientRepository(ClientRepositoryBase):
|
|||||||
obj.doId = id
|
obj.doId = id
|
||||||
self.doId2do[id] = obj
|
self.doId2do[id] = obj
|
||||||
obj.generateInit()
|
obj.generateInit()
|
||||||
|
obj._retrieveCachedData()
|
||||||
obj.generate()
|
obj.generate()
|
||||||
obj.announceGenerate()
|
obj.announceGenerate()
|
||||||
datagram = dclass.clientFormatGenerate(obj, id, zoneId, optionalFields)
|
datagram = dclass.clientFormatGenerate(obj, id, zoneId, optionalFields)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user