--- /dev/null
+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()