]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/ipc/StoreMap.cc
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 54 Interprocess Communication */
12 #include "ipc/StoreMap.h"
15 #include "store_key_md5.h"
19 StoreMapSlicesId(const SBuf
&path
)
21 return Ipc::Mem::Segment::Name(path
, "slices");
25 StoreMapAnchorsId(const SBuf
&path
)
27 return Ipc::Mem::Segment::Name(path
, "anchors");
30 Ipc::StoreMap::Owner
*
31 Ipc::StoreMap::Init(const SBuf
&path
, const int sliceLimit
)
33 assert(sliceLimit
> 0); // we should not be created otherwise
34 const int anchorLimit
= min(sliceLimit
, static_cast<int>(SwapFilenMax
));
35 Owner
*owner
= new Owner
;
36 owner
->anchors
= shm_new(Anchors
)(StoreMapAnchorsId(path
).c_str(), anchorLimit
);
37 owner
->slices
= shm_new(Slices
)(StoreMapSlicesId(path
).c_str(), sliceLimit
);
38 debugs(54, 5, "created " << path
<< " with " << anchorLimit
<< '+' << sliceLimit
);
42 Ipc::StoreMap::StoreMap(const SBuf
&aPath
): cleaner(NULL
), path(aPath
),
43 anchors(shm_old(Anchors
)(StoreMapAnchorsId(path
).c_str())),
44 slices(shm_old(Slices
)(StoreMapSlicesId(path
).c_str()))
46 debugs(54, 5, "attached " << path
<< " with " <<
47 anchors
->capacity
<< '+' << slices
->capacity
);
48 assert(entryLimit() > 0); // key-to-position mapping requires this
49 assert(entryLimit() <= sliceLimit()); // at least one slice per entry
53 Ipc::StoreMap::compareVersions(const sfileno fileno
, time_t newVersion
) const
55 const Anchor
&inode
= anchorAt(fileno
);
57 // note: we do not lock, so comparison may be inacurate
62 if (const time_t diff
= newVersion
- inode
.basics
.timestamp
)
63 return diff
< 0 ? -1 : +1;
69 Ipc::StoreMap::forgetWritingEntry(sfileno fileno
)
71 Anchor
&inode
= anchorAt(fileno
);
73 assert(inode
.writing());
75 // we do not iterate slices because we were told to forget about
76 // them; the caller is responsible for freeing them (most likely
77 // our slice list is incomplete or has holes)
79 inode
.waitingToBeFreed
= false;
82 inode
.lock
.unlockExclusive();
85 debugs(54, 8, "closed entry " << fileno
<< " for writing " << path
);
88 Ipc::StoreMap::Anchor
*
89 Ipc::StoreMap::openForWriting(const cache_key
*const key
, sfileno
&fileno
)
91 debugs(54, 5, "opening entry with key " << storeKeyText(key
)
92 << " for writing " << path
);
93 const int idx
= anchorIndexByKey(key
);
95 if (Anchor
*anchor
= openForWritingAt(idx
)) {
103 Ipc::StoreMap::Anchor
*
104 Ipc::StoreMap::openForWritingAt(const sfileno fileno
, bool overwriteExisting
)
106 Anchor
&s
= anchorAt(fileno
);
107 ReadWriteLock
&lock
= s
.lock
;
109 if (lock
.lockExclusive()) {
110 assert(s
.writing() && !s
.reading());
112 // bail if we cannot empty this position
113 if (!s
.waitingToBeFreed
&& !s
.empty() && !overwriteExisting
) {
114 lock
.unlockExclusive();
115 debugs(54, 5, "cannot open existing entry " << fileno
<<
116 " for writing " << path
);
120 // free if the entry was used, keeping the entry locked
121 if (s
.waitingToBeFreed
|| !s
.empty())
122 freeChain(fileno
, s
, true);
125 s
.start
= -1; // we have not allocated any slices yet
128 //s.setKey(key); // XXX: the caller should do that
129 debugs(54, 5, "opened entry " << fileno
<< " for writing " << path
);
130 return &s
; // and keep the entry locked
133 debugs(54, 5, "cannot open busy entry " << fileno
<<
134 " for writing " << path
);
139 Ipc::StoreMap::startAppending(const sfileno fileno
)
141 Anchor
&s
= anchorAt(fileno
);
143 s
.lock
.startAppending();
144 debugs(54, 5, "restricted entry " << fileno
<< " to appending " << path
);
148 Ipc::StoreMap::closeForWriting(const sfileno fileno
, bool lockForReading
)
150 Anchor
&s
= anchorAt(fileno
);
152 if (lockForReading
) {
153 s
.lock
.switchExclusiveToShared();
154 debugs(54, 5, "switched entry " << fileno
<<
155 " from writing to reading " << path
);
156 assert(s
.complete());
158 s
.lock
.unlockExclusive();
159 debugs(54, 5, "closed entry " << fileno
<< " for writing " << path
);
160 // cannot assert completeness here because we have no lock
164 Ipc::StoreMap::Slice
&
165 Ipc::StoreMap::writeableSlice(const AnchorId anchorId
, const SliceId sliceId
)
167 assert(anchorAt(anchorId
).writing());
168 assert(validSlice(sliceId
));
169 return sliceAt(sliceId
);
172 const Ipc::StoreMap::Slice
&
173 Ipc::StoreMap::readableSlice(const AnchorId anchorId
, const SliceId sliceId
) const
175 assert(anchorAt(anchorId
).reading());
176 assert(validSlice(sliceId
));
177 return sliceAt(sliceId
);
180 Ipc::StoreMap::Anchor
&
181 Ipc::StoreMap::writeableEntry(const AnchorId anchorId
)
183 assert(anchorAt(anchorId
).writing());
184 return anchorAt(anchorId
);
187 const Ipc::StoreMap::Anchor
&
188 Ipc::StoreMap::readableEntry(const AnchorId anchorId
) const
190 assert(anchorAt(anchorId
).reading());
191 return anchorAt(anchorId
);
195 Ipc::StoreMap::abortWriting(const sfileno fileno
)
197 debugs(54, 5, "aborting entry " << fileno
<< " for writing " << path
);
198 Anchor
&s
= anchorAt(fileno
);
200 s
.lock
.appending
= false; // locks out any new readers
201 if (!s
.lock
.readers
) {
202 freeChain(fileno
, s
, false);
203 debugs(54, 5, "closed clean entry " << fileno
<< " for writing " << path
);
205 s
.waitingToBeFreed
= true;
206 s
.lock
.unlockExclusive();
207 debugs(54, 5, "closed dirty entry " << fileno
<< " for writing " << path
);
211 const Ipc::StoreMap::Anchor
*
212 Ipc::StoreMap::peekAtReader(const sfileno fileno
) const
214 const Anchor
&s
= anchorAt(fileno
);
216 return &s
; // immediate access by lock holder so no locking
218 return NULL
; // the caller is not a read lock holder
219 assert(false); // must be locked for reading or writing
223 const Ipc::StoreMap::Anchor
&
224 Ipc::StoreMap::peekAtEntry(const sfileno fileno
) const
226 return anchorAt(fileno
);
230 Ipc::StoreMap::freeEntry(const sfileno fileno
)
232 debugs(54, 5, "marking entry " << fileno
<< " to be freed in " << path
);
234 Anchor
&s
= anchorAt(fileno
);
236 if (s
.lock
.lockExclusive())
237 freeChain(fileno
, s
, false);
239 s
.waitingToBeFreed
= true; // mark to free it later
243 Ipc::StoreMap::freeEntryByKey(const cache_key
*const key
)
245 debugs(54, 5, "marking entry with key " << storeKeyText(key
)
246 << " to be freed in " << path
);
248 const int idx
= anchorIndexByKey(key
);
249 Anchor
&s
= anchorAt(idx
);
250 if (s
.lock
.lockExclusive()) {
252 freeChain(idx
, s
, true);
253 s
.lock
.unlockExclusive();
254 } else if (s
.lock
.lockShared()) {
256 s
.waitingToBeFreed
= true; // mark to free it later
257 s
.lock
.unlockShared();
259 // we cannot be sure that the entry we found is ours because we do not
260 // have a lock on it, but we still check to minimize false deletions
262 s
.waitingToBeFreed
= true; // mark to free it later
266 /// unconditionally frees an already locked chain of slots, unlocking if needed
268 Ipc::StoreMap::freeChain(const sfileno fileno
, Anchor
&inode
, const bool keepLocked
)
270 debugs(54, 7, "freeing entry " << fileno
<<
272 if (!inode
.empty()) {
273 sfileno sliceId
= inode
.start
;
274 debugs(54, 8, "first slice " << sliceId
);
275 while (sliceId
>= 0) {
276 Slice
&slice
= sliceAt(sliceId
);
277 const sfileno nextId
= slice
.next
;
281 cleaner
->noteFreeMapSlice(sliceId
); // might change slice state
286 inode
.waitingToBeFreed
= false;
290 inode
.lock
.unlockExclusive();
292 debugs(54, 5, "freed entry " << fileno
<< " in " << path
);
295 const Ipc::StoreMap::Anchor
*
296 Ipc::StoreMap::openForReading(const cache_key
*const key
, sfileno
&fileno
)
298 debugs(54, 5, "opening entry with key " << storeKeyText(key
)
299 << " for reading " << path
);
300 const int idx
= anchorIndexByKey(key
);
301 if (const Anchor
*slot
= openForReadingAt(idx
)) {
302 if (slot
->sameKey(key
)) {
304 return slot
; // locked for reading
306 slot
->lock
.unlockShared();
307 debugs(54, 7, "closed entry " << idx
<< " for reading " << path
);
312 const Ipc::StoreMap::Anchor
*
313 Ipc::StoreMap::openForReadingAt(const sfileno fileno
)
315 debugs(54, 5, "opening entry " << fileno
<< " for reading " << path
);
316 Anchor
&s
= anchorAt(fileno
);
318 if (!s
.lock
.lockShared()) {
319 debugs(54, 5, "cannot open busy entry " << fileno
<<
320 " for reading " << path
);
325 s
.lock
.unlockShared();
326 debugs(54, 7, "cannot open empty entry " << fileno
<<
327 " for reading " << path
);
331 if (s
.waitingToBeFreed
) {
332 s
.lock
.unlockShared();
333 debugs(54, 7, "cannot open marked entry " << fileno
<<
334 " for reading " << path
);
338 debugs(54, 5, "opened entry " << fileno
<< " for reading " << path
);
343 Ipc::StoreMap::closeForReading(const sfileno fileno
)
345 Anchor
&s
= anchorAt(fileno
);
347 s
.lock
.unlockShared();
348 debugs(54, 5, "closed entry " << fileno
<< " for reading " << path
);
352 Ipc::StoreMap::purgeOne()
354 // Hopefully, we find a removable entry much sooner (TODO: use time?).
355 // The min() will protect us from division by zero inside the loop.
356 const int searchLimit
= min(10000, entryLimit());
358 for (; tries
< searchLimit
; ++tries
) {
359 const sfileno fileno
= static_cast<sfileno
>(++anchors
->victim
% entryLimit());
360 Anchor
&s
= anchorAt(fileno
);
361 if (s
.lock
.lockExclusive()) {
362 // the caller wants a free slice; empty anchor is not enough
363 if (!s
.empty() && s
.start
>= 0) {
364 // this entry may be marked for deletion, and that is OK
365 freeChain(fileno
, s
, false);
366 debugs(54, 5, "purged entry " << fileno
<< " from " << path
);
369 s
.lock
.unlockExclusive();
372 debugs(54, 5, "no entries to purge from " << path
<< "; tried: " << tries
);
377 Ipc::StoreMap::importSlice(const SliceId sliceId
, const Slice
&slice
)
379 // Slices are imported into positions that should not be available via
380 // "get free slice" API. This is not something we can double check
381 // reliably because the anchor for the imported slice may not have been
383 assert(validSlice(sliceId
));
384 sliceAt(sliceId
) = slice
;
388 Ipc::StoreMap::entryLimit() const
390 return min(sliceLimit(), static_cast<int>(SwapFilenMax
+1));
394 Ipc::StoreMap::entryCount() const
396 return anchors
->count
;
400 Ipc::StoreMap::sliceLimit() const
402 return slices
->capacity
;
406 Ipc::StoreMap::updateStats(ReadWriteLockStats
&stats
) const
408 for (int i
= 0; i
< anchors
->capacity
; ++i
)
409 anchorAt(i
).lock
.updateStats(stats
);
413 Ipc::StoreMap::validEntry(const int pos
) const
415 return 0 <= pos
&& pos
< entryLimit();
419 Ipc::StoreMap::validSlice(const int pos
) const
421 return 0 <= pos
&& pos
< sliceLimit();
424 Ipc::StoreMap::Anchor
&
425 Ipc::StoreMap::anchorAt(const sfileno fileno
)
427 assert(validEntry(fileno
));
428 return anchors
->items
[fileno
];
431 const Ipc::StoreMap::Anchor
&
432 Ipc::StoreMap::anchorAt(const sfileno fileno
) const
434 return const_cast<StoreMap
&>(*this).anchorAt(fileno
);
438 Ipc::StoreMap::anchorIndexByKey(const cache_key
*const key
) const
440 const uint64_t *const k
= reinterpret_cast<const uint64_t *>(key
);
441 // TODO: use a better hash function
442 return (k
[0] + k
[1]) % entryLimit();
445 Ipc::StoreMap::Anchor
&
446 Ipc::StoreMap::anchorByKey(const cache_key
*const key
)
448 return anchorAt(anchorIndexByKey(key
));
451 Ipc::StoreMap::Slice
&
452 Ipc::StoreMap::sliceAt(const SliceId sliceId
)
454 assert(validSlice(sliceId
));
455 return slices
->items
[sliceId
];
458 const Ipc::StoreMap::Slice
&
459 Ipc::StoreMap::sliceAt(const SliceId sliceId
) const
461 return const_cast<StoreMap
&>(*this).sliceAt(sliceId
);
464 /* Ipc::StoreMapAnchor */
466 Ipc::StoreMapAnchor::StoreMapAnchor(): start(0)
468 memset(&key
, 0, sizeof(key
));
469 memset(&basics
, 0, sizeof(basics
));
470 // keep in sync with rewind()
474 Ipc::StoreMapAnchor::setKey(const cache_key
*const aKey
)
476 memcpy(key
, aKey
, sizeof(key
));
480 Ipc::StoreMapAnchor::sameKey(const cache_key
*const aKey
) const
482 const uint64_t *const k
= reinterpret_cast<const uint64_t *>(aKey
);
483 return k
[0] == key
[0] && k
[1] == key
[1];
487 Ipc::StoreMapAnchor::set(const StoreEntry
&from
)
489 assert(writing() && !reading());
490 memcpy(key
, from
.key
, sizeof(key
));
491 basics
.timestamp
= from
.timestamp
;
492 basics
.lastref
= from
.lastref
;
493 basics
.expires
= from
.expires
;
494 basics
.lastmod
= from
.lastmod
;
495 basics
.swap_file_sz
= from
.swap_file_sz
;
496 basics
.refcount
= from
.refcount
;
497 basics
.flags
= from
.flags
;
501 Ipc::StoreMapAnchor::rewind()
505 memset(&key
, 0, sizeof(key
));
506 memset(&basics
, 0, sizeof(basics
));
510 Ipc::StoreMap::Owner::Owner(): anchors(NULL
), slices(NULL
)
514 Ipc::StoreMap::Owner::~Owner()
520 /* Ipc::StoreMapAnchors */
522 Ipc::StoreMapAnchors::StoreMapAnchors(const int aCapacity
):
531 Ipc::StoreMapAnchors::sharedMemorySize() const
533 return SharedMemorySize(capacity
);
537 Ipc::StoreMapAnchors::SharedMemorySize(const int capacity
)
539 return sizeof(StoreMapAnchors
) + capacity
* sizeof(StoreMapAnchor
);