]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ipc/MemMap.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / ipc / MemMap.cc
CommitLineData
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 16Ipc::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
26Ipc::MemMap::Owner *
27Ipc::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
35Ipc::MemMap::Owner *
36Ipc::MemMap::Init(const char *const path, const int limit)
37{
38 return Init(path, limit, 0);
39}
40
41Ipc::MemMap::Slot *
42Ipc::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
56Ipc::MemMap::Slot *
57Ipc::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
90void
91Ipc::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
105void
106Ipc::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
116const Ipc::MemMap::Slot *
117Ipc::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
129void
130Ipc::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
144const Ipc::MemMap::Slot *
145Ipc::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
165const Ipc::MemMap::Slot *
166Ipc::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
198void
199Ipc::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
209int
210Ipc::MemMap::entryLimit() const
211{
212 return shared->limit;
213}
214
215int
216Ipc::MemMap::entryCount() const
217{
218 return shared->count;
219}
220
221bool
222Ipc::MemMap::full() const
223{
224 return entryCount() >= entryLimit();
225}
226
227void
228Ipc::MemMap::updateStats(ReadWriteLockStats &stats) const
229{
230 for (int i = 0; i < shared->limit; ++i)
231 shared->slots[i].lock.updateStats(stats);
232}
233
234bool
235Ipc::MemMap::valid(const int pos) const
236{
237 return 0 <= pos && pos < entryLimit();
238}
239
240static
241unsigned int
242hash_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
253int
254Ipc::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
260Ipc::MemMap::Slot &
261Ipc::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
267void
268Ipc::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 283Ipc::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
291void
292Ipc::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
301bool
302Ipc::MemMapSlot::sameKey(const cache_key *const aKey) const
303{
304 return (memcmp(key, aKey, sizeof(key)) == 0);
305}
306
307bool
308Ipc::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
319Ipc::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
324Ipc::MemMap::Shared::~Shared()
325{
326}
327
328size_t
329Ipc::MemMap::Shared::sharedMemorySize() const
330{
331 return SharedMemorySize(limit, extrasSize);
332}
333
334size_t
335Ipc::MemMap::Shared::SharedMemorySize(const int limit, const size_t extrasSize)
336{
337 return sizeof(Shared) + limit * (sizeof(Slot) + extrasSize);
338}
f53969cc 339