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