X-Git-Url: http://dolda2000.com/gitweb/?p=didex.git;a=blobdiff_plain;f=didex%2Fcache.py;fp=didex%2Fcache.py;h=76742aa61ab54852885b5bdef1840ac2f953c464;hp=0000000000000000000000000000000000000000;hb=b080a59cbbe6b293d754bfa97c386eeede6b51ee;hpb=da5de0141d1328b254425b6a70d7a2c1f3c41c2b diff --git a/didex/cache.py b/didex/cache.py new file mode 100644 index 0000000..76742aa --- /dev/null +++ b/didex/cache.py @@ -0,0 +1,153 @@ +import threading, weakref + +class entry(object): + __slots__ = ["p", "n", "id", "obj", "st", "lk"] + def __init__(self, id, c): + self.id = id + self.obj = None + self.st = None + self.lk = None + self.n = c.mru + self.p = None + if c.mru is not None: + c.mru.p = self + c.mru = self + else: + c.mru = c.lru = self + c.n += 1 + + def relink(self, c): + if c.mru is self: + return + if self.n is not None: + self.n.p = self.p + self.p.n = self.n + if c.lru is self: + c.lru = self.p + self.p = None + self.n = c.mru + c.mru.p = self + + def remove(self, c): + if self.n is not None: + self.n.p = self.p + if self.p is not None: + self.p.n = self.n + if c.mru is self: + c.mru = self.n + if c.lru is self: + c.lru = self.p + c.n -= 1 + +class cache(object): + def __init__(self, *, keep=1000): + self.keep = keep + self.cur = {} + self.mru = self.lru = None + self.n = 0 + self.lk = threading.Lock() + + def _trim(self, n): + ent = self.lru + for i in range(self.n - n): + if ent.st == "l": + ent.obj = weakref.ref(ent.obj) + ent.st = "w" + elif ent.st == "w" and ent.obj() is None: + del self.cur[ent.id] + ent.remove(self) + ent.st = "r" + ent = ent.p + + def get(self, id, load=True): + while True: + with self.lk: + ent = self.cur.get(id) + if ent is None: + if not load: + raise KeyError(id) + self.cur[id] = ent = entry(id, self) + ent.lk = lk = threading.Lock() + ent.st = "ld" + st = None + self._trim(self.keep) + elif ent.st == "l": + ent.relink(self) + return ent.obj + elif ent.st == "w": + ret = ent.obj() + if ret is None: + del self.cur[id] + ent.remove(self) + ent.st = "r" + continue + return ret + elif ent.st == "ld": + lk = ent.lk + st = "ld" + if lk is None: + continue + elif ent.st == "r": + continue + with lk: + if st is None: + try: + ret = ent.obj = self.load(id) + ent.st = "l" + return ret + except: + with self.lk: + del self.cur[id] + ent.remove(self) + ent.st = "r" + raise + finally: + ent.lk = None + elif st == "ld": + continue + + def put(self, id, ob): + while True: + with self.lk: + ent = self.cur.get(id) + if ent is None: + self.cur[id] = ent = entry(id, self) + ent.obj = ob + ent.st = "l" + self._trim(self.keep) + return + elif ent.st == "l": + ent.obj = ob + return + elif ent.st == "w": + ent.obj = ob + return + elif ent.st == "r": + continue + elif ent.st == "ld": + lk = ent.lk + if lk is None: + continue + with lk: + continue + + def remove(self, id): + while True: + with self.lk: + ent = self.cur.get(id) + if ent is None: + return + elif ent.st == "ld": + lk = ent.lk + if lk is None: + continue + else: + del self.cur[id] + ent.remove(self) + ent.st = "r" + return + with lk: + continue + + def load(self, id): + raise KeyError()