Source code for radical.utils.object_cache


__author__    = 'Radical.Utils Development Team (Andre Merzky)'
__copyright__ = 'Copyright 2013, RADICAL@Rutgers'
__license__   = 'MIT'


import threading as mt

from .lockable  import Lockable
from .singleton import Singleton


# ------------------------------------------------------------------------------
#
# default timeout for delayed object removal.
#
_TIMEOUT    = 10
_DEFAULT_NS = 'global'


# ------------------------------------------------------------------------------
#
[docs]@Lockable class ObjectCache(object, metaclass=Singleton): ''' This is a singleton object caching class -- it maintains a reference counted registry of existing objects. ''' # TODO: we should introduce namespaces -- this is a singleton, but we may # want to use it in several places, thus need to make sure to not use # colliding names... # # FIXME: this class is not thread-safe! # -------------------------------------------------------------------------- # def __init__(self, timeout=_TIMEOUT): ''' Make sure the object cache dict is initialized, exactly once. If timeout is 0 or smaller, the objects are removed immediately -- otherwise removal is delayed by the specified timeout in seconds, to avoid thrashing on frequent removal/creation. ''' self._timeout = timeout print(1, timeout, self._timeout) self._cache = dict() # -------------------------------------------------------------------------- #
[docs] def get_obj(self, oid, creator, ns=_DEFAULT_NS): ''' For a given object id, attempt to retrieve an existing object. If that object exists, increase the reference counter, as there is now one more user for that object. If that object does not exist, call the given creator, then register and return the object thusly created. oid : id of the object to get from the cache. creator: method to use to create a new object instance Example: def creator(): return Logger(name) ret = object_cache.get_object(name, creator) ''' with self: if ns not in self._cache: self._cache[ns] = dict() ns_cache = self._cache[ns] oid = str(oid) if oid not in ns_cache: obj = creator() ns_cache[oid] = dict() ns_cache[oid]['cnt'] = 0 ns_cache[oid]['obj'] = obj ns_cache[oid]['cnt'] += 1 return ns_cache [oid]['obj']
# -------------------------------------------------------------------------- #
[docs] def rem_obj(self, obj, ns=_DEFAULT_NS): ''' For a given objects instance, decrease the refcounter as the caller stops using that object. Once the ref counter is '0', remove all traces of the object -- this should make that object eligable for Python's garbage collection. Returns 'True' if the given object was indeed registered, 'False' otherwise. The removal of the object is actually time-delayed. That way, we will keep the object around *just* a little longer, which provides caching semantics in the case of frequent creation/dstruction cycles. ''' with self: if ns not in self._cache: # impossible to find object return False ns_cache = self._cache[ns] for oid in ns_cache: if obj == ns_cache [oid]['obj']: print(self._timeout) if self._timeout: # delay actual removeal by _timeout seconds mt.Timer(self._timeout, self._rem_obj, [oid]).start() else: # immediate removeal self._rem_obj(oid) return True return False # obj not found
# -------------------------------------------------------------------------- # def _rem_obj(self, oid, ns=_DEFAULT_NS): ''' actual removal of an object(identified by oid) from the cache -- see :func:`rem_obj()` for details. ''' with self: assert ns in self._cache ns_cache = self._cache[ns] ns_cache [oid]['cnt'] -= 1 if ns_cache [oid]['cnt'] == 0: ns_cache [oid]['obj'] = None # free the obj reference ns_cache.pop(oid, None) # remove the cache entry
# ------------------------------------------------------------------------------