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