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 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

View File

@ -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)

View File

@ -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

View File

@ -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)