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