]>
Commit | Line | Data |
---|---|---|
44c95fcf | 1 | /* |
44c95fcf AR |
2 | * DEBUG: section 54 Interprocess Communication |
3 | */ | |
4 | ||
582c2af2 | 5 | #include "squid.h" |
5bed43d6 | 6 | #include "ipc/StoreMap.h" |
5bed43d6 | 7 | #include "Store.h" |
602d9612 | 8 | #include "store_key_md5.h" |
5bed43d6 | 9 | #include "tools.h" |
44c95fcf | 10 | |
68353d5a DK |
11 | Ipc::StoreMap::Owner * |
12 | Ipc::StoreMap::Init(const char *const path, const int limit, const size_t extrasSize) | |
44c95fcf | 13 | { |
69baf6b4 | 14 | assert(limit > 0); // we should not be created otherwise |
68353d5a | 15 | Owner *const owner = shm_new(Shared)(path, limit, extrasSize); |
9e92017a | 16 | debugs(54, 5, HERE << "new map [" << path << "] created: " << limit); |
68353d5a | 17 | return owner; |
44c95fcf AR |
18 | } |
19 | ||
68353d5a DK |
20 | Ipc::StoreMap::Owner * |
21 | Ipc::StoreMap::Init(const char *const path, const int limit) | |
44c95fcf | 22 | { |
68353d5a | 23 | return Init(path, limit, 0); |
44c95fcf AR |
24 | } |
25 | ||
68353d5a | 26 | Ipc::StoreMap::StoreMap(const char *const aPath): cleaner(NULL), path(aPath), |
9199139f | 27 | shared(shm_old(Shared)(aPath)) |
c011f9bc | 28 | { |
68353d5a DK |
29 | assert(shared->limit > 0); // we should not be created otherwise |
30 | debugs(54, 5, HERE << "attached map [" << path << "] created: " << | |
31 | shared->limit); | |
c011f9bc DK |
32 | } |
33 | ||
44c95fcf AR |
34 | Ipc::StoreMap::Slot * |
35 | Ipc::StoreMap::openForWriting(const cache_key *const key, sfileno &fileno) | |
36 | { | |
37 | debugs(54, 5, HERE << " trying to open slot for key " << storeKeyText(key) | |
38 | << " for writing in map [" << path << ']'); | |
39 | const int idx = slotIndexByKey(key); | |
40 | ||
41 | Slot &s = shared->slots[idx]; | |
42 | ReadWriteLock &lock = s.lock; | |
43 | ||
44 | if (lock.lockExclusive()) { | |
45 | assert(s.state != Slot::Writeable); // until we start breaking locks | |
46 | ||
7f6748c8 | 47 | // free if the entry was used, keeping the entry locked |
84bbc406 | 48 | if (s.waitingToBeFreed || s.state == Slot::Readable) |
44c95fcf AR |
49 | freeLocked(s, true); |
50 | ||
7f6748c8 AR |
51 | assert(s.state == Slot::Empty); |
52 | ++shared->count; | |
44c95fcf AR |
53 | s.state = Slot::Writeable; |
54 | fileno = idx; | |
55 | //s.setKey(key); // XXX: the caller should do that | |
56 | debugs(54, 5, HERE << " opened slot at " << idx << | |
57 | " for writing in map [" << path << ']'); | |
58 | return &s; // and keep the entry locked | |
59 | } | |
60 | ||
61 | debugs(54, 5, HERE << " failed to open slot at " << idx << | |
62 | " for writing in map [" << path << ']'); | |
63 | return NULL; | |
64 | } | |
65 | ||
66 | void | |
67 | Ipc::StoreMap::closeForWriting(const sfileno fileno, bool lockForReading) | |
68 | { | |
69 | debugs(54, 5, HERE << " closing slot at " << fileno << " for writing and " | |
70 | "openning for reading in map [" << path << ']'); | |
71 | assert(valid(fileno)); | |
72 | Slot &s = shared->slots[fileno]; | |
73 | assert(s.state == Slot::Writeable); | |
74 | s.state = Slot::Readable; | |
75 | if (lockForReading) | |
76 | s.lock.switchExclusiveToShared(); | |
77 | else | |
78 | s.lock.unlockExclusive(); | |
79 | } | |
80 | ||
81 | /// terminate writing the entry, freeing its slot for others to use | |
82 | void | |
83 | Ipc::StoreMap::abortWriting(const sfileno fileno) | |
84 | { | |
85 | debugs(54, 5, HERE << " abort writing slot at " << fileno << | |
86 | " in map [" << path << ']'); | |
87 | assert(valid(fileno)); | |
88 | Slot &s = shared->slots[fileno]; | |
89 | assert(s.state == Slot::Writeable); | |
90 | freeLocked(s, false); | |
91 | } | |
92 | ||
93 | void | |
94 | Ipc::StoreMap::abortIo(const sfileno fileno) | |
95 | { | |
96 | debugs(54, 5, HERE << " abort I/O for slot at " << fileno << | |
97 | " in map [" << path << ']'); | |
98 | assert(valid(fileno)); | |
99 | Slot &s = shared->slots[fileno]; | |
100 | ||
101 | // The caller is a lock holder. Thus, if we are Writeable, then the | |
102 | // caller must be the writer; otherwise the caller must be the reader. | |
103 | if (s.state == Slot::Writeable) | |
104 | abortWriting(fileno); | |
105 | else | |
106 | closeForReading(fileno); | |
107 | } | |
108 | ||
109 | const Ipc::StoreMap::Slot * | |
110 | Ipc::StoreMap::peekAtReader(const sfileno fileno) const | |
111 | { | |
112 | assert(valid(fileno)); | |
113 | const Slot &s = shared->slots[fileno]; | |
114 | switch (s.state) { | |
115 | case Slot::Readable: | |
116 | return &s; // immediate access by lock holder so no locking | |
117 | case Slot::Writeable: | |
118 | return NULL; // cannot read the slot when it is being written | |
119 | case Slot::Empty: | |
120 | assert(false); // must be locked for reading or writing | |
121 | } | |
122 | assert(false); // not reachable | |
123 | return NULL; | |
124 | } | |
125 | ||
126 | void | |
127 | Ipc::StoreMap::free(const sfileno fileno) | |
128 | { | |
129 | debugs(54, 5, HERE << " marking slot at " << fileno << " to be freed in" | |
9199139f | 130 | " map [" << path << ']'); |
44c95fcf AR |
131 | |
132 | assert(valid(fileno)); | |
133 | Slot &s = shared->slots[fileno]; | |
134 | ||
135 | if (s.lock.lockExclusive()) | |
136 | freeLocked(s, false); | |
137 | else | |
138 | s.waitingToBeFreed = true; // mark to free it later | |
139 | } | |
140 | ||
141 | const Ipc::StoreMap::Slot * | |
142 | Ipc::StoreMap::openForReading(const cache_key *const key, sfileno &fileno) | |
143 | { | |
144 | debugs(54, 5, HERE << " trying to open slot for key " << storeKeyText(key) | |
145 | << " for reading in map [" << path << ']'); | |
146 | const int idx = slotIndexByKey(key); | |
147 | if (const Slot *slot = openForReadingAt(idx)) { | |
148 | if (slot->sameKey(key)) { | |
149 | fileno = idx; | |
150 | debugs(54, 5, HERE << " opened slot at " << fileno << " for key " | |
151 | << storeKeyText(key) << " for reading in map [" << path << | |
152 | ']'); | |
153 | return slot; // locked for reading | |
154 | } | |
155 | slot->lock.unlockShared(); | |
156 | } | |
157 | debugs(54, 5, HERE << " failed to open slot for key " << storeKeyText(key) | |
158 | << " for reading in map [" << path << ']'); | |
159 | return NULL; | |
160 | } | |
161 | ||
162 | const Ipc::StoreMap::Slot * | |
163 | Ipc::StoreMap::openForReadingAt(const sfileno fileno) | |
164 | { | |
165 | debugs(54, 5, HERE << " trying to open slot at " << fileno << " for " | |
166 | "reading in map [" << path << ']'); | |
167 | assert(valid(fileno)); | |
168 | Slot &s = shared->slots[fileno]; | |
169 | ||
170 | if (!s.lock.lockShared()) { | |
171 | debugs(54, 5, HERE << " failed to lock slot at " << fileno << " for " | |
172 | "reading in map [" << path << ']'); | |
173 | return NULL; | |
174 | } | |
175 | ||
176 | if (s.state == Slot::Empty) { | |
177 | s.lock.unlockShared(); | |
178 | debugs(54, 7, HERE << " empty slot at " << fileno << " for " | |
179 | "reading in map [" << path << ']'); | |
180 | return NULL; | |
181 | } | |
182 | ||
183 | if (s.waitingToBeFreed) { | |
184 | s.lock.unlockShared(); | |
185 | debugs(54, 7, HERE << " dirty slot at " << fileno << " for " | |
186 | "reading in map [" << path << ']'); | |
187 | return NULL; | |
188 | } | |
189 | ||
190 | // cannot be Writing here if we got shared lock and checked Empty above | |
191 | assert(s.state == Slot::Readable); | |
192 | debugs(54, 5, HERE << " opened slot at " << fileno << " for reading in" | |
193 | " map [" << path << ']'); | |
194 | return &s; | |
195 | } | |
196 | ||
197 | void | |
198 | Ipc::StoreMap::closeForReading(const sfileno fileno) | |
199 | { | |
200 | debugs(54, 5, HERE << " closing slot at " << fileno << " for reading in " | |
201 | "map [" << path << ']'); | |
202 | assert(valid(fileno)); | |
203 | Slot &s = shared->slots[fileno]; | |
204 | assert(s.state == Slot::Readable); | |
205 | s.lock.unlockShared(); | |
206 | } | |
207 | ||
208 | int | |
209 | Ipc::StoreMap::entryLimit() const | |
210 | { | |
211 | return shared->limit; | |
212 | } | |
213 | ||
214 | int | |
215 | Ipc::StoreMap::entryCount() const | |
216 | { | |
217 | return shared->count; | |
218 | } | |
219 | ||
220 | bool | |
221 | Ipc::StoreMap::full() const | |
222 | { | |
223 | return entryCount() >= entryLimit(); | |
224 | } | |
225 | ||
226 | void | |
227 | Ipc::StoreMap::updateStats(ReadWriteLockStats &stats) const | |
228 | { | |
229 | for (int i = 0; i < shared->limit; ++i) | |
230 | shared->slots[i].lock.updateStats(stats); | |
231 | } | |
232 | ||
233 | bool | |
234 | Ipc::StoreMap::valid(const int pos) const | |
235 | { | |
236 | return 0 <= pos && pos < entryLimit(); | |
237 | } | |
238 | ||
239 | int | |
240 | Ipc::StoreMap::slotIndexByKey(const cache_key *const key) const | |
241 | { | |
242 | const uint64_t *const k = reinterpret_cast<const uint64_t *>(key); | |
243 | // TODO: use a better hash function | |
244 | return (k[0] + k[1]) % shared->limit; | |
245 | } | |
246 | ||
247 | Ipc::StoreMap::Slot & | |
248 | Ipc::StoreMap::slotByKey(const cache_key *const key) | |
249 | { | |
250 | return shared->slots[slotIndexByKey(key)]; | |
251 | } | |
252 | ||
253 | /// unconditionally frees the already exclusively locked slot and releases lock | |
254 | void | |
255 | Ipc::StoreMap::freeLocked(Slot &s, bool keepLocked) | |
256 | { | |
7f6748c8 | 257 | if (s.state == Slot::Readable && cleaner) |
3a8c5551 | 258 | cleaner->cleanReadable(&s - shared->slots.raw()); |
7f6748c8 | 259 | |
44c95fcf AR |
260 | s.waitingToBeFreed = false; |
261 | s.state = Slot::Empty; | |
262 | if (!keepLocked) | |
263 | s.lock.unlockExclusive(); | |
264 | --shared->count; | |
3a8c5551 | 265 | debugs(54, 5, HERE << " freed slot at " << (&s - shared->slots.raw()) << |
44c95fcf AR |
266 | " in map [" << path << ']'); |
267 | } | |
268 | ||
44c95fcf AR |
269 | /* Ipc::StoreMapSlot */ |
270 | ||
271 | Ipc::StoreMapSlot::StoreMapSlot(): state(Empty) | |
272 | { | |
e297be13 AJ |
273 | memset(&key, 0, sizeof(key)); |
274 | memset(&basics, 0, sizeof(basics)); | |
44c95fcf AR |
275 | } |
276 | ||
277 | void | |
278 | Ipc::StoreMapSlot::setKey(const cache_key *const aKey) | |
279 | { | |
280 | memcpy(key, aKey, sizeof(key)); | |
281 | } | |
282 | ||
283 | bool | |
284 | Ipc::StoreMapSlot::sameKey(const cache_key *const aKey) const | |
285 | { | |
286 | const uint64_t *const k = reinterpret_cast<const uint64_t *>(aKey); | |
287 | return k[0] == key[0] && k[1] == key[1]; | |
288 | } | |
289 | ||
290 | void | |
291 | Ipc::StoreMapSlot::set(const StoreEntry &from) | |
292 | { | |
293 | memcpy(key, from.key, sizeof(key)); | |
294 | // XXX: header = aHeader; | |
295 | basics.timestamp = from.timestamp; | |
296 | basics.lastref = from.lastref; | |
297 | basics.expires = from.expires; | |
298 | basics.lastmod = from.lastmod; | |
299 | basics.swap_file_sz = from.swap_file_sz; | |
300 | basics.refcount = from.refcount; | |
301 | basics.flags = from.flags; | |
302 | } | |
303 | ||
304 | /* Ipc::StoreMap::Shared */ | |
305 | ||
68353d5a | 306 | Ipc::StoreMap::Shared::Shared(const int aLimit, const size_t anExtrasSize): |
3a8c5551 | 307 | limit(aLimit), extrasSize(anExtrasSize), count(0), slots(aLimit) |
68353d5a DK |
308 | { |
309 | } | |
310 | ||
311 | size_t | |
312 | Ipc::StoreMap::Shared::sharedMemorySize() const | |
44c95fcf | 313 | { |
68353d5a | 314 | return SharedMemorySize(limit, extrasSize); |
44c95fcf AR |
315 | } |
316 | ||
317 | size_t | |
68353d5a | 318 | Ipc::StoreMap::Shared::SharedMemorySize(const int limit, const size_t extrasSize) |
44c95fcf | 319 | { |
68353d5a | 320 | return sizeof(Shared) + limit * (sizeof(Slot) + extrasSize); |
44c95fcf AR |
321 | } |
322 |