]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipc/StoreMap.cc
merge from parent SslServerCertValidator r12337
[thirdparty/squid.git] / src / ipc / StoreMap.cc
1 /*
2 * DEBUG: section 54 Interprocess Communication
3 */
4
5 #include "squid.h"
6 #include "ipc/StoreMap.h"
7 #include "store_key_md5.h"
8 #include "Store.h"
9 #include "tools.h"
10
11 Ipc::StoreMap::Owner *
12 Ipc::StoreMap::Init(const char *const path, const int limit, const size_t extrasSize)
13 {
14 assert(limit > 0); // we should not be created otherwise
15 Owner *const owner = shm_new(Shared)(path, limit, extrasSize);
16 debugs(54, 5, HERE << "new map [" << path << "] created: " << limit);
17 return owner;
18 }
19
20 Ipc::StoreMap::Owner *
21 Ipc::StoreMap::Init(const char *const path, const int limit)
22 {
23 return Init(path, limit, 0);
24 }
25
26 Ipc::StoreMap::StoreMap(const char *const aPath): cleaner(NULL), path(aPath),
27 shared(shm_old(Shared)(aPath))
28 {
29 assert(shared->limit > 0); // we should not be created otherwise
30 debugs(54, 5, HERE << "attached map [" << path << "] created: " <<
31 shared->limit);
32 }
33
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
47 // free if the entry was used, keeping the entry locked
48 if (s.waitingToBeFreed || s.state == Slot::Readable)
49 freeLocked(s, true);
50
51 assert(s.state == Slot::Empty);
52 ++shared->count;
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"
130 " map [" << path << ']');
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 {
257 if (s.state == Slot::Readable && cleaner)
258 cleaner->cleanReadable(&s - shared->slots.raw());
259
260 s.waitingToBeFreed = false;
261 s.state = Slot::Empty;
262 if (!keepLocked)
263 s.lock.unlockExclusive();
264 --shared->count;
265 debugs(54, 5, HERE << " freed slot at " << (&s - shared->slots.raw()) <<
266 " in map [" << path << ']');
267 }
268
269 /* Ipc::StoreMapSlot */
270
271 Ipc::StoreMapSlot::StoreMapSlot(): state(Empty)
272 {
273 memset(&key, 0, sizeof(key));
274 memset(&basics, 0, sizeof(basics));
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
306 Ipc::StoreMap::Shared::Shared(const int aLimit, const size_t anExtrasSize):
307 limit(aLimit), extrasSize(anExtrasSize), count(0), slots(aLimit)
308 {
309 }
310
311 size_t
312 Ipc::StoreMap::Shared::sharedMemorySize() const
313 {
314 return SharedMemorySize(limit, extrasSize);
315 }
316
317 size_t
318 Ipc::StoreMap::Shared::SharedMemorySize(const int limit, const size_t extrasSize)
319 {
320 return sizeof(Shared) + limit * (sizeof(Slot) + extrasSize);
321 }
322