+ def flush(self):
+ return self.obj.flush()
+
+
+class FileHandle(Handle):
+ """Connects a numeric file handle to a File object that has
+ been opened by the client."""
+ pass
+
+
+class DirectoryHandle(Handle):
+ """Connects a numeric file handle to a Directory object that has
+ been opened by the client."""
+
+ def __init__(self, fh, dirobj, entries):
+ super(DirectoryHandle, self).__init__(fh, dirobj)
+ self.entries = entries
+
+
+class InodeCache(object):
+ """Records the memory footprint of objects and when they are last used.
+
+ When the cache limit is exceeded, the least recently used objects are
+ cleared. Clearing the object means discarding its contents to release
+ memory. The next time the object is accessed, it must be re-fetched from
+ the server. Note that the inode cache limit is a soft limit; the cache
+ limit may be exceeded if necessary to load very large objects, it may also
+ be exceeded if open file handles prevent objects from being cleared.
+
+ """
+
+ def __init__(self, cap, min_entries=4):
+ self._entries = collections.OrderedDict()
+ self._by_uuid = {}
+ self._counter = itertools.count(0)
+ self.cap = cap
+ self._total = 0
+ self.min_entries = min_entries
+
+ def total(self):
+ return self._total
+
+ def _remove(self, obj, clear):
+ if clear and not obj.clear():
+ _logger.debug("InodeCache could not clear %i in_use %s", obj.inode, obj.in_use())
+ return False
+ self._total -= obj.cache_size
+ del self._entries[obj.cache_priority]
+ if obj.cache_uuid:
+ del self._by_uuid[obj.cache_uuid]
+ obj.cache_uuid = None
+ if clear:
+ _logger.debug("InodeCache cleared %i total now %i", obj.inode, self._total)
+ return True
+
+ def cap_cache(self):
+ if self._total > self.cap:
+ for key in list(self._entries.keys()):
+ if self._total < self.cap or len(self._entries) < self.min_entries:
+ break
+ self._remove(self._entries[key], True)
+
+ def manage(self, obj):
+ if obj.persisted():
+ obj.cache_priority = next(self._counter)
+ obj.cache_size = obj.objsize()
+ self._entries[obj.cache_priority] = obj
+ obj.cache_uuid = obj.uuid()
+ if obj.cache_uuid:
+ self._by_uuid[obj.cache_uuid] = obj
+ self._total += obj.objsize()
+ _logger.debug("InodeCache touched %i (size %i) total now %i", obj.inode, obj.objsize(), self._total)
+ self.cap_cache()
+ else:
+ obj.cache_priority = None
+
+ def touch(self, obj):
+ if obj.persisted():
+ if obj.cache_priority in self._entries:
+ self._remove(obj, False)
+ self.manage(obj)
+
+ def unmanage(self, obj):
+ if obj.persisted() and obj.cache_priority in self._entries:
+ self._remove(obj, True)
+
+ def find(self, uuid):
+ return self._by_uuid.get(uuid)