]>
Commit | Line | Data |
---|---|---|
f28b12e7 | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
bbc27441 AJ |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
f28b12e7 CT |
7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 54 Interprocess Communication */ |
10 | ||
f28b12e7 CT |
11 | #include "squid.h" |
12 | #include "ipc/MemMap.h" | |
13 | #include "store_key_md5.h" | |
14 | #include "tools.h" | |
15 | ||
f321944d | 16 | Ipc::MemMap::MemMap(const char *const aPath) : |
f53969cc SM |
17 | cleaner(NULL), |
18 | path(aPath), | |
19 | shared(shm_old(Shared)(aPath)) | |
f28b12e7 CT |
20 | { |
21 | assert(shared->limit > 0); // we should not be created otherwise | |
22 | debugs(54, 5, "attached map [" << path << "] created: " << | |
23 | shared->limit); | |
24 | } | |
25 | ||
26 | Ipc::MemMap::Owner * | |
27 | Ipc::MemMap::Init(const char *const path, const int limit, const size_t extrasSize) | |
28 | { | |
29 | assert(limit > 0); // we should not be created otherwise | |
30 | Owner *const owner = shm_new(Shared)(path, limit, extrasSize); | |
31 | debugs(54, 5, "new map [" << path << "] created: " << limit); | |
32 | return owner; | |
33 | } | |
34 | ||
35 | Ipc::MemMap::Owner * | |
36 | Ipc::MemMap::Init(const char *const path, const int limit) | |
37 | { | |
38 | return Init(path, limit, 0); | |
39 | } | |
40 | ||
41 | Ipc::MemMap::Slot * | |
42 | Ipc::MemMap::openForWriting(const cache_key *const key, sfileno &fileno) | |
43 | { | |
44 | debugs(54, 5, "trying to open slot for key " << storeKeyText(key) | |
45 | << " for writing in map [" << path << ']'); | |
46 | const int idx = slotIndexByKey(key); | |
47 | ||
48 | if (Slot *slot = openForWritingAt(idx)) { | |
49 | fileno = idx; | |
50 | return slot; | |
51 | } | |
52 | ||
53 | return NULL; | |
54 | } | |
55 | ||
56 | Ipc::MemMap::Slot * | |
57 | Ipc::MemMap::openForWritingAt(const sfileno fileno, bool overwriteExisting) | |
58 | { | |
59 | Slot &s = shared->slots[fileno]; | |
60 | ReadWriteLock &lock = s.lock; | |
61 | ||
62 | if (lock.lockExclusive()) { | |
63 | assert(s.writing() && !s.reading()); | |
64 | ||
65 | // bail if we cannot empty this position | |
66 | if (!s.waitingToBeFreed && !s.empty() && !overwriteExisting) { | |
67 | lock.unlockExclusive(); | |
68 | debugs(54, 5, "cannot open existing entry " << fileno << | |
69 | " for writing " << path); | |
70 | return NULL; | |
71 | } | |
72 | ||
73 | // free if the entry was used, keeping the entry locked | |
74 | if (s.waitingToBeFreed || !s.empty()) | |
75 | freeLocked(s, true); | |
76 | ||
77 | assert(s.empty()); | |
78 | ++shared->count; | |
79 | ||
80 | debugs(54, 5, "opened slot at " << fileno << | |
81 | " for writing in map [" << path << ']'); | |
82 | return &s; // and keep the entry locked | |
83 | } | |
84 | ||
85 | debugs(54, 5, "failed to open slot at " << fileno << | |
86 | " for writing in map [" << path << ']'); | |
87 | return NULL; | |
88 | } | |
89 | ||
90 | void | |
91 | Ipc::MemMap::closeForWriting(const sfileno fileno, bool lockForReading) | |
92 | { | |
93 | debugs(54, 5, "closing slot at " << fileno << " for writing and " | |
94 | "openning for reading in map [" << path << ']'); | |
95 | assert(valid(fileno)); | |
96 | Slot &s = shared->slots[fileno]; | |
97 | assert(s.writing()); | |
98 | if (lockForReading) | |
99 | s.lock.switchExclusiveToShared(); | |
100 | else | |
101 | s.lock.unlockExclusive(); | |
102 | } | |
103 | ||
104 | /// terminate writing the entry, freeing its slot for others to use | |
105 | void | |
106 | Ipc::MemMap::abortWriting(const sfileno fileno) | |
107 | { | |
108 | debugs(54, 5, "abort writing slot at " << fileno << | |
109 | " in map [" << path << ']'); | |
110 | assert(valid(fileno)); | |
111 | Slot &s = shared->slots[fileno]; | |
112 | assert(s.writing()); | |
113 | freeLocked(s, false); | |
114 | } | |
115 | ||
116 | const Ipc::MemMap::Slot * | |
117 | Ipc::MemMap::peekAtReader(const sfileno fileno) const | |
118 | { | |
119 | assert(valid(fileno)); | |
120 | const Slot &s = shared->slots[fileno]; | |
121 | if (s.reading()) | |
122 | return &s; // immediate access by lock holder so no locking | |
123 | if (s.writing()) | |
124 | return NULL; // cannot read the slot when it is being written | |
125 | assert(false); // must be locked for reading or writing | |
126 | return NULL; | |
127 | } | |
128 | ||
129 | void | |
130 | Ipc::MemMap::free(const sfileno fileno) | |
131 | { | |
132 | debugs(54, 5, "marking slot at " << fileno << " to be freed in" | |
133 | " map [" << path << ']'); | |
134 | ||
135 | assert(valid(fileno)); | |
136 | Slot &s = shared->slots[fileno]; | |
137 | ||
138 | if (s.lock.lockExclusive()) | |
139 | freeLocked(s, false); | |
140 | else | |
141 | s.waitingToBeFreed = true; // mark to free it later | |
142 | } | |
143 | ||
144 | const Ipc::MemMap::Slot * | |
145 | Ipc::MemMap::openForReading(const cache_key *const key, sfileno &fileno) | |
146 | { | |
147 | debugs(54, 5, "trying to open slot for key " << storeKeyText(key) | |
148 | << " for reading in map [" << path << ']'); | |
149 | const int idx = slotIndexByKey(key); | |
150 | if (const Slot *slot = openForReadingAt(idx)) { | |
151 | if (slot->sameKey(key)) { | |
152 | fileno = idx; | |
153 | debugs(54, 5, "opened slot at " << fileno << " for key " | |
154 | << storeKeyText(key) << " for reading in map [" << path << | |
155 | ']'); | |
156 | return slot; // locked for reading | |
157 | } | |
158 | slot->lock.unlockShared(); | |
159 | } | |
160 | debugs(54, 5, "failed to open slot for key " << storeKeyText(key) | |
161 | << " for reading in map [" << path << ']'); | |
162 | return NULL; | |
163 | } | |
164 | ||
165 | const Ipc::MemMap::Slot * | |
166 | Ipc::MemMap::openForReadingAt(const sfileno fileno) | |
167 | { | |
168 | debugs(54, 5, "trying to open slot at " << fileno << " for " | |
169 | "reading in map [" << path << ']'); | |
170 | assert(valid(fileno)); | |
171 | Slot &s = shared->slots[fileno]; | |
172 | ||
173 | if (!s.lock.lockShared()) { | |
174 | debugs(54, 5, "failed to lock slot at " << fileno << " for " | |
175 | "reading in map [" << path << ']'); | |
176 | return NULL; | |
177 | } | |
178 | ||
179 | if (s.empty()) { | |
180 | s.lock.unlockShared(); | |
181 | debugs(54, 7, "empty slot at " << fileno << " for " | |
182 | "reading in map [" << path << ']'); | |
183 | return NULL; | |
184 | } | |
185 | ||
186 | if (s.waitingToBeFreed) { | |
187 | s.lock.unlockShared(); | |
188 | debugs(54, 7, "dirty slot at " << fileno << " for " | |
189 | "reading in map [" << path << ']'); | |
190 | return NULL; | |
191 | } | |
192 | ||
193 | debugs(54, 5, "opened slot at " << fileno << " for reading in" | |
194 | " map [" << path << ']'); | |
195 | return &s; | |
196 | } | |
197 | ||
198 | void | |
199 | Ipc::MemMap::closeForReading(const sfileno fileno) | |
200 | { | |
201 | debugs(54, 5, "closing slot at " << fileno << " for reading in " | |
202 | "map [" << path << ']'); | |
203 | assert(valid(fileno)); | |
204 | Slot &s = shared->slots[fileno]; | |
205 | assert(s.reading()); | |
206 | s.lock.unlockShared(); | |
207 | } | |
208 | ||
209 | int | |
210 | Ipc::MemMap::entryLimit() const | |
211 | { | |
212 | return shared->limit; | |
213 | } | |
214 | ||
215 | int | |
216 | Ipc::MemMap::entryCount() const | |
217 | { | |
218 | return shared->count; | |
219 | } | |
220 | ||
221 | bool | |
222 | Ipc::MemMap::full() const | |
223 | { | |
224 | return entryCount() >= entryLimit(); | |
225 | } | |
226 | ||
227 | void | |
228 | Ipc::MemMap::updateStats(ReadWriteLockStats &stats) const | |
229 | { | |
230 | for (int i = 0; i < shared->limit; ++i) | |
231 | shared->slots[i].lock.updateStats(stats); | |
232 | } | |
233 | ||
234 | bool | |
235 | Ipc::MemMap::valid(const int pos) const | |
236 | { | |
237 | return 0 <= pos && pos < entryLimit(); | |
238 | } | |
239 | ||
240 | static | |
241 | unsigned int | |
242 | hash_key(const unsigned char *data, unsigned int len, unsigned int hashSize) | |
243 | { | |
244 | unsigned int n; | |
245 | unsigned int j; | |
86c63190 | 246 | for (j = 0, n = 0; j < len; j++ ) { |
f28b12e7 CT |
247 | n ^= 271 * *data; |
248 | ++data; | |
249 | } | |
250 | return (n ^ (j * 271)) % hashSize; | |
251 | } | |
252 | ||
253 | int | |
254 | Ipc::MemMap::slotIndexByKey(const cache_key *const key) const | |
255 | { | |
256 | const unsigned char *k = reinterpret_cast<const unsigned char *>(key); | |
257 | return hash_key(k, MEMMAP_SLOT_KEY_SIZE, shared->limit); | |
258 | } | |
259 | ||
260 | Ipc::MemMap::Slot & | |
261 | Ipc::MemMap::slotByKey(const cache_key *const key) | |
262 | { | |
263 | return shared->slots[slotIndexByKey(key)]; | |
264 | } | |
265 | ||
266 | /// unconditionally frees the already exclusively locked slot and releases lock | |
267 | void | |
268 | Ipc::MemMap::freeLocked(Slot &s, bool keepLocked) | |
269 | { | |
270 | if (!s.empty() && cleaner) | |
271 | cleaner->noteFreeMapSlot(&s - shared->slots.raw()); | |
272 | ||
273 | s.waitingToBeFreed = false; | |
274 | memset(s.key, 0, sizeof(s.key)); | |
275 | if (!keepLocked) | |
276 | s.lock.unlockExclusive(); | |
277 | --shared->count; | |
278 | debugs(54, 5, "freed slot at " << (&s - shared->slots.raw()) << | |
279 | " in map [" << path << ']'); | |
280 | } | |
281 | ||
282 | /* Ipc::MemMapSlot */ | |
f321944d | 283 | Ipc::MemMapSlot::MemMapSlot() : |
f53969cc SM |
284 | pSize(0), |
285 | expire(0) | |
f28b12e7 CT |
286 | { |
287 | memset(key, 0, sizeof(key)); | |
288 | memset(p, 0, sizeof(p)); | |
f28b12e7 CT |
289 | } |
290 | ||
291 | void | |
292 | Ipc::MemMapSlot::set(const unsigned char *aKey, const void *block, size_t blockSize, time_t expireAt) | |
293 | { | |
294 | memcpy(key, aKey, sizeof(key)); | |
295 | if (block) | |
296 | memcpy(p, block, blockSize); | |
297 | pSize = blockSize; | |
298 | expire = expireAt; | |
299 | } | |
300 | ||
301 | bool | |
302 | Ipc::MemMapSlot::sameKey(const cache_key *const aKey) const | |
303 | { | |
304 | return (memcmp(key, aKey, sizeof(key)) == 0); | |
305 | } | |
306 | ||
307 | bool | |
308 | Ipc::MemMapSlot::empty() const | |
309 | { | |
310 | for (unsigned char const*u = key; u < key + sizeof(key); ++u) { | |
311 | if (*u) | |
312 | return false; | |
313 | } | |
314 | return true; | |
315 | } | |
316 | ||
317 | /* Ipc::MemMap::Shared */ | |
318 | ||
319 | Ipc::MemMap::Shared::Shared(const int aLimit, const size_t anExtrasSize): | |
f53969cc | 320 | limit(aLimit), extrasSize(anExtrasSize), count(0), slots(aLimit) |
f28b12e7 CT |
321 | { |
322 | } | |
323 | ||
324 | Ipc::MemMap::Shared::~Shared() | |
325 | { | |
326 | } | |
327 | ||
328 | size_t | |
329 | Ipc::MemMap::Shared::sharedMemorySize() const | |
330 | { | |
331 | return SharedMemorySize(limit, extrasSize); | |
332 | } | |
333 | ||
334 | size_t | |
335 | Ipc::MemMap::Shared::SharedMemorySize(const int limit, const size_t extrasSize) | |
336 | { | |
337 | return sizeof(Shared) + limit * (sizeof(Slot) + extrasSize); | |
338 | } | |
f53969cc | 339 |