added CRDataCache

This commit is contained in:
Darren Ranalli 2008-05-21 18:29:13 +00:00
parent 91147411ee
commit ef0ba78f1d
6 changed files with 178 additions and 0 deletions

View 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

View 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

View File

@ -58,6 +58,7 @@ class ClientRepository(ClientRepositoryBase):
obj.doId = id
self.doId2do[id] = obj
obj.generateInit()
obj._retrieveCachedData()
obj.generate()
obj.announceGenerate()
datagram = dclass.clientFormatGenerate(obj, id, zoneId, optionalFields)
@ -201,6 +202,7 @@ class ClientRepository(ClientRepositoryBase):
self.doId2do[doId] = distObj
# Update the required fields
distObj.generateInit() # Only called when constructed
distObj._retrieveCachedData()
distObj.generate()
distObj.updateRequiredFields(dclass, di)
# updateRequiredFields calls announceGenerate

View File

@ -3,6 +3,7 @@ from MsgTypes import *
from direct.task import Task
from direct.directnotify import DirectNotifyGlobal
import CRCache
from direct.distributed.CRDataCache import CRDataCache
from direct.distributed.ConnectionRepository import ConnectionRepository
from direct.showbase import PythonUtil
import ParentMgr
@ -44,6 +45,7 @@ class ClientRepositoryBase(ConnectionRepository):
self.readDCFile(dcFileNames)
self.cache=CRCache.CRCache()
self.doDataCache = CRDataCache()
self.cacheOwner=CRCache.CRCache()
self.serverDelta = 0
@ -365,6 +367,7 @@ class ClientRepositoryBase(ConnectionRepository):
self.doId2do[doId] = distObj
# Update the required fields
distObj.generateInit() # Only called when constructed
distObj._retrieveCachedData()
distObj.generate()
distObj.setLocation(parentId, zoneId)
distObj.updateRequiredFields(dclass, di)
@ -412,6 +415,7 @@ class ClientRepositoryBase(ConnectionRepository):
self.doId2do[doId] = distObj
# Update the required fields
distObj.generateInit() # Only called when constructed
distObj._retrieveCachedData()
distObj.generate()
distObj.setLocation(parentId, zoneId)
distObj.updateRequiredOtherFields(dclass, di)

View File

@ -154,6 +154,35 @@ class DistributedObject(DistributedObjectBase):
def getNeverDisable(self):
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):
assert bool == 1 or bool == 0
self.cacheable = bool
@ -272,6 +301,13 @@ class DistributedObject(DistributedObjectBase):
# after this is called, the object is no longer a DistributedObject
# but may still be used as a DelayDeleted object
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.dclass = None

View File

@ -58,6 +58,7 @@ class OldClientRepository(ClientRepositoryBase):
obj.doId = id
self.doId2do[id] = obj
obj.generateInit()
obj._retrieveCachedData()
obj.generate()
obj.announceGenerate()
datagram = dclass.clientFormatGenerate(obj, id, zoneId, optionalFields)