138 lines
4.9 KiB
Python
138 lines
4.9 KiB
Python
|
"""CRCache module: contains the CRCache class"""
|
||
|
|
||
|
from direct.directnotify import DirectNotifyGlobal
|
||
|
from . import DistributedObject
|
||
|
|
||
|
class CRCache:
|
||
|
notify = DirectNotifyGlobal.directNotify.newCategory("CRCache")
|
||
|
|
||
|
def __init__(self, maxCacheItems=10):
|
||
|
self.maxCacheItems = maxCacheItems
|
||
|
self.storedCacheItems = maxCacheItems
|
||
|
self.dict = {}
|
||
|
self.fifo = []
|
||
|
|
||
|
def isEmpty(self):
|
||
|
return len(self.fifo) == 0
|
||
|
|
||
|
def flush(self):
|
||
|
"""
|
||
|
Delete each item in the cache then clear all references to them
|
||
|
"""
|
||
|
assert self.checkCache()
|
||
|
CRCache.notify.debug("Flushing the cache")
|
||
|
# NOTE: delayDeleted objects should no longer get into the cache in the first place
|
||
|
# give objects a chance to clean themselves up before checking for DelayDelete leaks
|
||
|
messenger.send('clientCleanup')
|
||
|
# some of these objects might be holding delayDeletes on others
|
||
|
# track each object that is delayDeleted after it gets its chance to delete,
|
||
|
# and check them after all objects have had a chance to delete
|
||
|
delayDeleted = []
|
||
|
for distObj in self.dict.values():
|
||
|
distObj.deleteOrDelay()
|
||
|
if distObj.getDelayDeleteCount() != 0:
|
||
|
delayDeleted.append(distObj)
|
||
|
if distObj.getDelayDeleteCount() <= 0:
|
||
|
# make sure we're not leaking
|
||
|
distObj.detectLeaks()
|
||
|
# now that all objects have had a chance to delete, are there any objects left
|
||
|
# that are still delayDeleted?
|
||
|
delayDeleteLeaks = []
|
||
|
for distObj in delayDeleted:
|
||
|
if distObj.getDelayDeleteCount() != 0:
|
||
|
delayDeleteLeaks.append(distObj)
|
||
|
if len(delayDeleteLeaks):
|
||
|
s = 'CRCache.flush:'
|
||
|
for obj in delayDeleteLeaks:
|
||
|
s += ('\n could not delete %s (%s), delayDeletes=%s' %
|
||
|
(safeRepr(obj), itype(obj), obj.getDelayDeleteNames()))
|
||
|
self.notify.error(s)
|
||
|
# Null out all references to the objects so they will get gcd
|
||
|
self.dict = {}
|
||
|
self.fifo = []
|
||
|
|
||
|
def cache(self, distObj):
|
||
|
# Only distributed objects are allowed in the cache
|
||
|
assert isinstance(distObj, DistributedObject.DistributedObject)
|
||
|
assert self.checkCache()
|
||
|
# Get the doId
|
||
|
doId = distObj.getDoId()
|
||
|
# Error check
|
||
|
success = False
|
||
|
if doId in self.dict:
|
||
|
CRCache.notify.warning("Double cache attempted for distObj "
|
||
|
+ str(doId))
|
||
|
else:
|
||
|
# Call disable on the distObj
|
||
|
distObj.disableAndAnnounce()
|
||
|
|
||
|
# Put the distObj in the fifo and the dict
|
||
|
self.fifo.append(distObj)
|
||
|
self.dict[doId] = distObj
|
||
|
|
||
|
success = True
|
||
|
|
||
|
if len(self.fifo) > self.maxCacheItems:
|
||
|
# if the cache is full, pop the oldest item
|
||
|
oldestDistObj = self.fifo.pop(0)
|
||
|
# and remove it from the dictionary
|
||
|
del(self.dict[oldestDistObj.getDoId()])
|
||
|
# and delete it
|
||
|
oldestDistObj.deleteOrDelay()
|
||
|
if oldestDistObj.getDelayDeleteCount() <= 0:
|
||
|
# make sure we're not leaking
|
||
|
oldestDistObj.detectLeaks()
|
||
|
|
||
|
# Make sure that the fifo and the dictionary are sane
|
||
|
assert len(self.dict) == len(self.fifo)
|
||
|
return success
|
||
|
|
||
|
def retrieve(self, doId):
|
||
|
assert self.checkCache()
|
||
|
if doId in self.dict:
|
||
|
# Find the object
|
||
|
distObj = self.dict[doId]
|
||
|
# Remove it from the dictionary
|
||
|
del(self.dict[doId])
|
||
|
# Remove it from the fifo
|
||
|
self.fifo.remove(distObj)
|
||
|
# return the distObj
|
||
|
return distObj
|
||
|
else:
|
||
|
# If you can't find it, return None
|
||
|
return None
|
||
|
|
||
|
def contains(self, doId):
|
||
|
return doId in self.dict
|
||
|
|
||
|
def delete(self, doId):
|
||
|
assert self.checkCache()
|
||
|
assert doId in self.dict
|
||
|
# Look it up
|
||
|
distObj = self.dict[doId]
|
||
|
# Remove it from the dict and fifo
|
||
|
del(self.dict[doId])
|
||
|
self.fifo.remove(distObj)
|
||
|
# and delete it
|
||
|
distObj.deleteOrDelay()
|
||
|
if distObj.getDelayDeleteCount() <= 0:
|
||
|
# make sure we're not leaking
|
||
|
distObj.detectLeaks()
|
||
|
|
||
|
def checkCache(self):
|
||
|
# For debugging; this verifies that the cache is sensible and
|
||
|
# returns true if so.
|
||
|
from panda3d.core import NodePath
|
||
|
for obj in self.dict.values():
|
||
|
if isinstance(obj, NodePath):
|
||
|
assert not obj.isEmpty() and obj.getTopNode() != render.node()
|
||
|
return 1
|
||
|
|
||
|
def turnOff(self):
|
||
|
self.flush()
|
||
|
self.storedMaxCache = self.maxCacheItems
|
||
|
self.maxCacheItems = 0
|
||
|
|
||
|
def turnOn(self):
|
||
|
self.maxCacheItems = self.storedMaxCache
|