Added live-object store and simple indexing.
[didex.git] / didex / cache.py
CommitLineData
b080a59c
FT
1import threading, weakref
2
3class entry(object):
4 __slots__ = ["p", "n", "id", "obj", "st", "lk"]
5 def __init__(self, id, c):
6 self.id = id
7 self.obj = None
8 self.st = None
9 self.lk = None
10 self.n = c.mru
11 self.p = None
12 if c.mru is not None:
13 c.mru.p = self
14 c.mru = self
15 else:
16 c.mru = c.lru = self
17 c.n += 1
18
19 def relink(self, c):
20 if c.mru is self:
21 return
22 if self.n is not None:
23 self.n.p = self.p
24 self.p.n = self.n
25 if c.lru is self:
26 c.lru = self.p
27 self.p = None
28 self.n = c.mru
29 c.mru.p = self
30
31 def remove(self, c):
32 if self.n is not None:
33 self.n.p = self.p
34 if self.p is not None:
35 self.p.n = self.n
36 if c.mru is self:
37 c.mru = self.n
38 if c.lru is self:
39 c.lru = self.p
40 c.n -= 1
41
42class cache(object):
43 def __init__(self, *, keep=1000):
44 self.keep = keep
45 self.cur = {}
46 self.mru = self.lru = None
47 self.n = 0
48 self.lk = threading.Lock()
49
50 def _trim(self, n):
51 ent = self.lru
52 for i in range(self.n - n):
53 if ent.st == "l":
54 ent.obj = weakref.ref(ent.obj)
55 ent.st = "w"
56 elif ent.st == "w" and ent.obj() is None:
57 del self.cur[ent.id]
58 ent.remove(self)
59 ent.st = "r"
60 ent = ent.p
61
62 def get(self, id, load=True):
63 while True:
64 with self.lk:
65 ent = self.cur.get(id)
66 if ent is None:
67 if not load:
68 raise KeyError(id)
69 self.cur[id] = ent = entry(id, self)
70 ent.lk = lk = threading.Lock()
71 ent.st = "ld"
72 st = None
73 self._trim(self.keep)
74 elif ent.st == "l":
75 ent.relink(self)
76 return ent.obj
77 elif ent.st == "w":
78 ret = ent.obj()
79 if ret is None:
80 del self.cur[id]
81 ent.remove(self)
82 ent.st = "r"
83 continue
84 return ret
85 elif ent.st == "ld":
86 lk = ent.lk
87 st = "ld"
88 if lk is None:
89 continue
90 elif ent.st == "r":
91 continue
92 with lk:
93 if st is None:
94 try:
95 ret = ent.obj = self.load(id)
96 ent.st = "l"
97 return ret
98 except:
99 with self.lk:
100 del self.cur[id]
101 ent.remove(self)
102 ent.st = "r"
103 raise
104 finally:
105 ent.lk = None
106 elif st == "ld":
107 continue
108
109 def put(self, id, ob):
110 while True:
111 with self.lk:
112 ent = self.cur.get(id)
113 if ent is None:
114 self.cur[id] = ent = entry(id, self)
115 ent.obj = ob
116 ent.st = "l"
117 self._trim(self.keep)
118 return
119 elif ent.st == "l":
120 ent.obj = ob
121 return
122 elif ent.st == "w":
123 ent.obj = ob
124 return
125 elif ent.st == "r":
126 continue
127 elif ent.st == "ld":
128 lk = ent.lk
129 if lk is None:
130 continue
131 with lk:
132 continue
133
134 def remove(self, id):
135 while True:
136 with self.lk:
137 ent = self.cur.get(id)
138 if ent is None:
139 return
140 elif ent.st == "ld":
141 lk = ent.lk
142 if lk is None:
143 continue
144 else:
145 del self.cur[id]
146 ent.remove(self)
147 ent.st = "r"
148 return
149 with lk:
150 continue
151
152 def load(self, id):
153 raise KeyError()