]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipc/MemMap.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / ipc / MemMap.cc
1 /*
2 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
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.
7 */
8
9 /* DEBUG: section 54 Interprocess Communication */
10
11 #include "squid.h"
12 #include "ipc/MemMap.h"
13 #include "store_key_md5.h"
14 #include "tools.h"
15
16 Ipc::MemMap::MemMap(const char *const aPath) :
17 cleaner(NULL),
18 path(aPath),
19 shared(shm_old(Shared)(aPath))
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;
246 for (j = 0, n = 0; j < len; j++ ) {
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 */
283 Ipc::MemMapSlot::MemMapSlot() :
284 pSize(0),
285 expire(0)
286 {
287 memset(key, 0, sizeof(key));
288 memset(p, 0, sizeof(p));
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):
320 limit(aLimit), extrasSize(anExtrasSize), count(0), slots(aLimit)
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 }
339