]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/ipc/StoreMap.cc
2 * Copyright (C) 1996-2021 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"
13 #include "sbuf/SBuf.h"
15 #include "store/Controller.h"
16 #include "store_key_md5.h"
20 StoreMapSlicesId(const SBuf
&path
)
22 return Ipc::Mem::Segment::Name(path
, "slices");
26 StoreMapAnchorsId(const SBuf
&path
)
28 return Ipc::Mem::Segment::Name(path
, "anchors");
32 StoreMapFileNosId(const SBuf
&path
)
34 return Ipc::Mem::Segment::Name(path
, "filenos");
37 Ipc::StoreMap::Owner
*
38 Ipc::StoreMap::Init(const SBuf
&path
, const int sliceLimit
)
40 assert(sliceLimit
> 0); // we should not be created otherwise
41 const int anchorLimit
= min(sliceLimit
, static_cast<int>(SwapFilenMax
));
42 Owner
*owner
= new Owner
;
43 owner
->fileNos
= shm_new(FileNos
)(StoreMapFileNosId(path
).c_str(), anchorLimit
);
44 owner
->anchors
= shm_new(Anchors
)(StoreMapAnchorsId(path
).c_str(), anchorLimit
);
45 owner
->slices
= shm_new(Slices
)(StoreMapSlicesId(path
).c_str(), sliceLimit
);
46 debugs(54, 5, "created " << path
<< " with " << anchorLimit
<< '+' << sliceLimit
);
50 Ipc::StoreMap::StoreMap(const SBuf
&aPath
): cleaner(NULL
), path(aPath
),
51 fileNos(shm_old(FileNos
)(StoreMapFileNosId(path
).c_str())),
52 anchors(shm_old(Anchors
)(StoreMapAnchorsId(path
).c_str())),
53 slices(shm_old(Slices
)(StoreMapSlicesId(path
).c_str()))
55 debugs(54, 5, "attached " << path
<< " with " <<
56 fileNos
->capacity
<< '+' <<
57 anchors
->capacity
<< '+' << slices
->capacity
);
58 assert(entryLimit() > 0); // key-to-position mapping requires this
59 assert(entryLimit() <= sliceLimit()); // at least one slice per entry
63 Ipc::StoreMap::compareVersions(const sfileno fileno
, time_t newVersion
) const
65 const Anchor
&inode
= anchorAt(fileno
);
67 // note: we do not lock, so comparison may be inacurate
72 if (const time_t diff
= newVersion
- inode
.basics
.timestamp
)
73 return diff
< 0 ? -1 : +1;
79 Ipc::StoreMap::forgetWritingEntry(sfileno fileno
)
81 Anchor
&inode
= anchorAt(fileno
);
83 assert(inode
.writing());
85 // we do not iterate slices because we were told to forget about
86 // them; the caller is responsible for freeing them (most likely
87 // our slice list is incomplete or has holes)
91 inode
.lock
.unlockExclusive();
94 debugs(54, 8, "closed entry " << fileno
<< " for writing " << path
);
97 const Ipc::StoreMap::Anchor
*
98 Ipc::StoreMap::openOrCreateForReading(const cache_key
*const key
, sfileno
&fileno
, const StoreEntry
&entry
)
100 debugs(54, 5, "opening/creating entry with key " << storeKeyText(key
)
101 << " for reading " << path
);
103 // start with reading so that we do not overwrite an existing unlocked entry
104 auto idx
= fileNoByKey(key
);
105 if (const auto anchor
= openForReadingAt(idx
, key
)) {
110 // the competing openOrCreateForReading() workers race to create a new entry
111 idx
= fileNoByKey(key
);
112 if (auto anchor
= openForWritingAt(idx
)) {
113 anchor
->set(entry
, key
);
114 anchor
->lock
.switchExclusiveToShared();
116 assert(anchor
->complete());
118 debugs(54, 5, "switched entry " << fileno
<< " from writing to reading " << path
);
122 // we lost the above race; see if the winner-created entry is now readable
123 // TODO: Do some useful housekeeping work here to give the winner more time.
124 idx
= fileNoByKey(key
);
125 if (const auto anchor
= openForReadingAt(idx
, key
)) {
130 // slow entry creator or some other problem
134 Ipc::StoreMap::Anchor
*
135 Ipc::StoreMap::openForWriting(const cache_key
*const key
, sfileno
&fileno
)
137 debugs(54, 5, "opening entry with key " << storeKeyText(key
)
138 << " for writing " << path
);
139 const int idx
= fileNoByKey(key
);
141 if (Anchor
*anchor
= openForWritingAt(idx
)) {
149 Ipc::StoreMap::Anchor
*
150 Ipc::StoreMap::openForWritingAt(const sfileno fileno
, bool overwriteExisting
)
152 Anchor
&s
= anchorAt(fileno
);
153 ReadWriteLock
&lock
= s
.lock
;
155 if (lock
.lockExclusive()) {
156 assert(s
.writing() && !s
.reading());
158 // bail if we cannot empty this position
159 if (!s
.waitingToBeFreed
&& !s
.empty() && !overwriteExisting
) {
160 lock
.unlockExclusive();
161 debugs(54, 5, "cannot open existing entry " << fileno
<<
162 " for writing " << path
);
166 // free if the entry was used, keeping the entry locked
167 if (s
.waitingToBeFreed
|| !s
.empty())
168 freeChain(fileno
, s
, true);
171 s
.start
= -1; // we have not allocated any slices yet
172 s
.splicingPoint
= -1;
175 //s.setKey(key); // XXX: the caller should do that
176 debugs(54, 5, "opened entry " << fileno
<< " for writing " << path
);
177 return &s
; // and keep the entry locked
180 debugs(54, 5, "cannot open busy entry " << fileno
<<
181 " for writing " << path
);
186 Ipc::StoreMap::startAppending(const sfileno fileno
)
188 Anchor
&s
= anchorAt(fileno
);
190 s
.lock
.startAppending();
191 debugs(54, 5, "restricted entry " << fileno
<< " to appending " << path
);
195 Ipc::StoreMap::closeForWriting(const sfileno fileno
)
197 Anchor
&s
= anchorAt(fileno
);
199 // TODO: assert(!s.empty()); // i.e., unlocked s becomes s.complete()
200 s
.lock
.unlockExclusive();
201 debugs(54, 5, "closed entry " << fileno
<< " for writing " << path
);
202 // cannot assert completeness here because we have no lock
206 Ipc::StoreMap::switchWritingToReading(const sfileno fileno
)
208 debugs(54, 5, "switching entry " << fileno
<< " from writing to reading " << path
);
209 Anchor
&s
= anchorAt(fileno
);
211 s
.lock
.switchExclusiveToShared();
212 assert(s
.complete());
215 Ipc::StoreMap::Slice
&
216 Ipc::StoreMap::writeableSlice(const AnchorId anchorId
, const SliceId sliceId
)
218 assert(anchorAt(anchorId
).writing());
219 assert(validSlice(sliceId
));
220 return sliceAt(sliceId
);
223 const Ipc::StoreMap::Slice
&
224 Ipc::StoreMap::readableSlice(const AnchorId anchorId
, const SliceId sliceId
) const
226 assert(anchorAt(anchorId
).reading());
227 assert(validSlice(sliceId
));
228 return sliceAt(sliceId
);
231 Ipc::StoreMap::Anchor
&
232 Ipc::StoreMap::writeableEntry(const AnchorId anchorId
)
234 assert(anchorAt(anchorId
).writing());
235 return anchorAt(anchorId
);
238 const Ipc::StoreMap::Anchor
&
239 Ipc::StoreMap::readableEntry(const AnchorId anchorId
) const
241 assert(anchorAt(anchorId
).reading());
242 return anchorAt(anchorId
);
246 Ipc::StoreMap::abortWriting(const sfileno fileno
)
248 debugs(54, 5, "aborting entry " << fileno
<< " for writing " << path
);
249 Anchor
&s
= anchorAt(fileno
);
251 s
.lock
.appending
= false; // locks out any new readers
252 if (!s
.lock
.readers
) {
253 freeChain(fileno
, s
, false);
254 debugs(54, 5, "closed clean entry " << fileno
<< " for writing " << path
);
256 s
.waitingToBeFreed
= true;
257 s
.writerHalted
= true;
258 s
.lock
.unlockExclusive();
259 debugs(54, 5, "closed dirty entry " << fileno
<< " for writing " << path
);
264 Ipc::StoreMap::abortUpdating(Update
&update
)
266 const sfileno fileno
= update
.stale
.fileNo
;
267 debugs(54, 5, "aborting entry " << fileno
<< " for updating " << path
);
269 AssertFlagIsSet(update
.stale
.anchor
->lock
.updating
);
270 update
.stale
.anchor
->lock
.unlockHeaders();
271 closeForReading(update
.stale
.fileNo
);
272 update
.stale
= Update::Edition();
275 abortWriting(update
.fresh
.fileNo
);
276 update
.fresh
= Update::Edition();
278 debugs(54, 5, "aborted entry " << fileno
<< " for updating " << path
);
281 const Ipc::StoreMap::Anchor
*
282 Ipc::StoreMap::peekAtReader(const sfileno fileno
) const
284 const Anchor
&s
= anchorAt(fileno
);
286 return &s
; // immediate access by lock holder so no locking
287 assert(s
.writing()); // must be locked for reading or writing
291 const Ipc::StoreMap::Anchor
*
292 Ipc::StoreMap::peekAtWriter(const sfileno fileno
) const
294 const Anchor
&s
= anchorAt(fileno
);
296 return &s
; // immediate access by lock holder so no locking
297 assert(s
.reading()); // must be locked for reading or writing
301 const Ipc::StoreMap::Anchor
&
302 Ipc::StoreMap::peekAtEntry(const sfileno fileno
) const
304 return anchorAt(fileno
);
308 Ipc::StoreMap::freeEntry(const sfileno fileno
)
310 debugs(54, 5, "marking entry " << fileno
<< " to be freed in " << path
);
312 Anchor
&s
= anchorAt(fileno
);
314 if (s
.lock
.lockExclusive()) {
315 const bool result
= !s
.waitingToBeFreed
&& !s
.empty();
316 freeChain(fileno
, s
, false);
320 uint8_t expected
= false;
321 // mark to free the locked entry later (if not already marked)
322 return s
.waitingToBeFreed
.compare_exchange_strong(expected
, true);
326 Ipc::StoreMap::freeEntryByKey(const cache_key
*const key
)
328 debugs(54, 5, "marking entry with key " << storeKeyText(key
)
329 << " to be freed in " << path
);
331 const int idx
= fileNoByKey(key
);
332 Anchor
&s
= anchorAt(idx
);
333 if (s
.lock
.lockExclusive()) {
335 freeChain(idx
, s
, true);
336 s
.lock
.unlockExclusive();
337 } else if (s
.lock
.lockShared()) {
339 s
.waitingToBeFreed
= true; // mark to free it later
340 s
.lock
.unlockShared();
342 // we cannot be sure that the entry we found is ours because we do not
343 // have a lock on it, but we still check to minimize false deletions
345 s
.waitingToBeFreed
= true; // mark to free it later
350 Ipc::StoreMap::markedForDeletion(const cache_key
*const key
)
352 const int idx
= fileNoByKey(key
);
353 const Anchor
&s
= anchorAt(idx
);
354 return s
.sameKey(key
) ? bool(s
.waitingToBeFreed
) : false;
358 Ipc::StoreMap::hasReadableEntry(const cache_key
*const key
)
361 if (openForReading(reinterpret_cast<const cache_key
*>(key
), index
)) {
362 closeForReading(index
);
368 /// unconditionally frees an already locked chain of slots, unlocking if needed
370 Ipc::StoreMap::freeChain(const sfileno fileno
, Anchor
&inode
, const bool keepLocked
)
372 debugs(54, 7, "freeing entry " << fileno
<<
375 freeChainAt(inode
.start
, inode
.splicingPoint
);
379 inode
.lock
.unlockExclusive();
381 debugs(54, 5, "freed entry " << fileno
<< " in " << path
);
384 /// unconditionally frees an already locked chain of slots; no anchor maintenance
386 Ipc::StoreMap::freeChainAt(SliceId sliceId
, const SliceId splicingPoint
)
388 static uint64_t ChainId
= 0; // to pair freeing/freed calls in debugs()
389 const uint64_t chainId
= ++ChainId
;
390 debugs(54, 7, "freeing chain #" << chainId
<< " starting at " << sliceId
<< " in " << path
);
391 while (sliceId
>= 0) {
392 Slice
&slice
= sliceAt(sliceId
);
393 const SliceId nextId
= slice
.next
;
396 cleaner
->noteFreeMapSlice(sliceId
); // might change slice state
397 if (sliceId
== splicingPoint
) {
398 debugs(54, 5, "preserving chain #" << chainId
<< " in " << path
<<
399 " suffix after slice " << splicingPoint
);
400 break; // do not free the rest of the chain
404 debugs(54, 7, "freed chain #" << chainId
<< " in " << path
);
408 Ipc::StoreMap::prepFreeSlice(const SliceId sliceId
)
410 // TODO: Move freeSlots here, along with reserveSlotForWriting() logic.
411 assert(validSlice(sliceId
));
412 sliceAt(sliceId
).clear();
415 Ipc::StoreMap::SliceId
416 Ipc::StoreMap::sliceContaining(const sfileno fileno
, const uint64_t bytesNeeded
) const
418 const Anchor
&anchor
= anchorAt(fileno
);
419 Must(anchor
.reading());
420 uint64_t bytesSeen
= 0;
421 SliceId lastSlice
= anchor
.start
;
422 while (lastSlice
>= 0) {
423 const Slice
&slice
= sliceAt(lastSlice
);
424 bytesSeen
+= slice
.size
;
425 if (bytesSeen
>= bytesNeeded
)
427 lastSlice
= slice
.next
;
429 debugs(54, 7, "entry " << fileno
<< " has " << bytesNeeded
<< '/' << bytesSeen
<<
430 " bytes at slice " << lastSlice
<< " in " << path
);
431 return lastSlice
; // may be negative
434 const Ipc::StoreMap::Anchor
*
435 Ipc::StoreMap::openForReading(const cache_key
*const key
, sfileno
&fileno
)
437 debugs(54, 5, "opening entry with key " << storeKeyText(key
)
438 << " for reading " << path
);
439 const int idx
= fileNoByKey(key
);
440 if (const auto anchor
= openForReadingAt(idx
, key
)) {
442 return anchor
; // locked for reading
447 const Ipc::StoreMap::Anchor
*
448 Ipc::StoreMap::openForReadingAt(const sfileno fileno
, const cache_key
*const key
)
450 debugs(54, 5, "opening entry " << fileno
<< " for reading " << path
);
451 Anchor
&s
= anchorAt(fileno
);
453 if (!s
.lock
.lockShared()) {
454 debugs(54, 5, "cannot open busy entry " << fileno
<<
455 " for reading " << path
);
460 s
.lock
.unlockShared();
461 debugs(54, 7, "cannot open empty entry " << fileno
<<
462 " for reading " << path
);
466 if (s
.waitingToBeFreed
) {
467 s
.lock
.unlockShared();
468 debugs(54, 7, "cannot open marked entry " << fileno
<<
469 " for reading " << path
);
473 if (!s
.sameKey(key
)) {
474 s
.lock
.unlockShared();
475 debugs(54, 5, "cannot open wrong-key entry " << fileno
<<
476 " for reading " << path
);
480 debugs(54, 5, "opened entry " << fileno
<< " for reading " << path
);
485 Ipc::StoreMap::closeForReading(const sfileno fileno
)
487 Anchor
&s
= anchorAt(fileno
);
489 s
.lock
.unlockShared();
490 debugs(54, 5, "closed entry " << fileno
<< " for reading " << path
);
494 Ipc::StoreMap::closeForReadingAndFreeIdle(const sfileno fileno
)
496 auto &s
= anchorAt(fileno
);
499 if (!s
.lock
.unlockSharedAndSwitchToExclusive()) {
500 debugs(54, 5, "closed entry " << fileno
<< " for reading " << path
);
505 assert(!s
.reading());
506 freeChain(fileno
, s
, false);
507 debugs(54, 5, "closed idle entry " << fileno
<< " for reading " << path
);
511 Ipc::StoreMap::openForUpdating(Update
&update
, const sfileno fileNoHint
)
514 const StoreEntry
&entry
= *update
.entry
;
515 const cache_key
*const key
= reinterpret_cast<const cache_key
*>(entry
.key
);
516 update
.stale
.name
= nameByKey(key
);
518 if (!validEntry(fileNoHint
)) {
519 debugs(54, 5, "opening entry with key " << storeKeyText(key
) <<
520 " for updating " << path
);
521 update
.stale
.fileNo
= fileNoByName(update
.stale
.name
);
523 update
.stale
.fileNo
= fileNoHint
;
526 debugs(54, 5, "opening entry " << update
.stale
.fileNo
<< " of " << entry
<< " for updating " << path
);
528 // Unreadable entries cannot (e.g., empty and otherwise problematic entries)
529 // or should not (e.g., entries still forming their metadata) be updated.
530 if (!openForReadingAt(update
.stale
.fileNo
, key
)) {
531 debugs(54, 5, "cannot open unreadable entry " << update
.stale
.fileNo
<< " for updating " << path
);
535 update
.stale
.anchor
= &anchorAt(update
.stale
.fileNo
);
536 if (update
.stale
.anchor
->writing()) {
537 // TODO: Support updating appending entries.
538 // For example, MemStore::updateHeaders() would not know how
539 // many old prefix body bytes to copy to the new prefix if the last old
540 // prefix slice has not been formed yet (i.e., still gets more bytes).
541 debugs(54, 5, "cannot open appending entry " << update
.stale
.fileNo
<<
542 " for updating " << path
);
543 closeForReading(update
.stale
.fileNo
);
547 if (!update
.stale
.anchor
->lock
.lockHeaders()) {
548 debugs(54, 5, "cannot open updating entry " << update
.stale
.fileNo
<<
549 " for updating " << path
);
550 closeForReading(update
.stale
.fileNo
);
554 /* stale anchor is properly locked; we can now use abortUpdating() if needed */
556 if (!openKeyless(update
.fresh
)) {
557 debugs(54, 5, "cannot open freshchainless entry " << update
.stale
.fileNo
<<
558 " for updating " << path
);
559 abortUpdating(update
);
565 update
.fresh
.anchor
->set(entry
);
566 debugs(54, 5, "opened entry " << update
.stale
.fileNo
<< " for updating " << path
<<
567 " using entry " << update
.fresh
.fileNo
<< " of " << entry
);
572 /// finds an anchor that is currently not associated with any entry key and
573 /// locks it for writing so ensure exclusive access during updates
575 Ipc::StoreMap::openKeyless(Update::Edition
&edition
)
577 return visitVictims([&](const sfileno name
) {
578 Update::Edition temp
;
580 temp
.fileNo
= fileNoByName(temp
.name
);
581 if ((temp
.anchor
= openForWritingAt(temp
.fileNo
))) {
582 debugs(54, 5, "created entry " << temp
.fileNo
<<
583 " for updating " << path
);
593 Ipc::StoreMap::closeForUpdating(Update
&update
)
595 Must(update
.stale
.anchor
);
596 Must(update
.fresh
.anchor
);
597 AssertFlagIsSet(update
.stale
.anchor
->lock
.updating
);
598 Must(update
.stale
.splicingPoint
>= 0);
599 Must(update
.fresh
.splicingPoint
>= 0);
601 /* the stale prefix cannot overlap with the fresh one (a weak check) */
602 Must(update
.stale
.anchor
->start
!= update
.fresh
.anchor
->start
);
603 Must(update
.stale
.anchor
->start
!= update
.fresh
.splicingPoint
);
604 Must(update
.stale
.splicingPoint
!= update
.fresh
.anchor
->start
);
605 Must(update
.stale
.splicingPoint
!= update
.fresh
.splicingPoint
);
607 /* the relative order of most operations is significant here */
609 /* splice the fresh chain prefix with the stale chain suffix */
610 Slice
&freshSplicingSlice
= sliceAt(update
.fresh
.splicingPoint
);
611 const SliceId suffixStart
= sliceAt(update
.stale
.splicingPoint
).next
; // may be negative
612 // the fresh chain is either properly terminated or already spliced
613 if (freshSplicingSlice
.next
< 0)
614 freshSplicingSlice
.next
= suffixStart
;
616 Must(freshSplicingSlice
.next
== suffixStart
);
617 // either way, fresh chain uses the stale chain suffix now
619 // make the fresh anchor/chain readable for everybody
620 update
.fresh
.anchor
->lock
.switchExclusiveToShared();
621 // but the fresh anchor is still invisible to anybody but us
623 // This freeEntry() code duplicates the code below to minimize the time when
624 // the freeEntry() race condition (see the Race: comment below) might occur.
625 if (update
.stale
.anchor
->waitingToBeFreed
)
626 freeEntry(update
.fresh
.fileNo
);
628 /* any external changes were applied to the stale anchor/chain until now */
629 relocate(update
.stale
.name
, update
.fresh
.fileNo
);
630 /* any external changes will apply to the fresh anchor/chain from now on */
632 // Race: If the stale entry was deleted by some kid during the assignment,
633 // then we propagate that event to the fresh anchor and chain. Since this
634 // update is not atomically combined with the assignment above, another kid
635 // might get a fresh entry just before we have a chance to free it. However,
636 // such deletion races are always possible even without updates.
637 if (update
.stale
.anchor
->waitingToBeFreed
)
638 freeEntry(update
.fresh
.fileNo
);
640 /* free the stale chain prefix except for the shared suffix */
641 update
.stale
.anchor
->splicingPoint
= update
.stale
.splicingPoint
;
642 freeEntry(update
.stale
.fileNo
);
644 // Make the stale anchor/chain reusable, reachable via update.fresh.name. If
645 // update.entry->swap_filen is still update.stale.fileNo, and the entry is
646 // using store, then the entry must have a lock on update.stale.fileNo,
647 // preventing its premature reuse by others.
648 relocate(update
.fresh
.name
, update
.stale
.fileNo
);
650 const Update updateSaved
= update
; // for post-close debugging below
652 /* unlock the stale anchor/chain */
653 update
.stale
.anchor
->lock
.unlockHeaders();
654 closeForReading(update
.stale
.fileNo
);
655 update
.stale
= Update::Edition();
657 // finally, unlock the fresh entry
658 closeForReading(update
.fresh
.fileNo
);
659 update
.fresh
= Update::Edition();
661 debugs(54, 5, "closed entry " << updateSaved
.stale
.fileNo
<< " of " << *updateSaved
.entry
<<
662 " named " << updateSaved
.stale
.name
<< " for updating " << path
<<
663 " to fresh entry " << updateSaved
.fresh
.fileNo
<< " named " << updateSaved
.fresh
.name
<<
664 " with [" << updateSaved
.fresh
.anchor
->start
<< ',' << updateSaved
.fresh
.splicingPoint
<<
665 "] prefix containing at least " << freshSplicingSlice
.size
<< " bytes");
668 /// Visits entries until either
669 /// * the `visitor` returns true (indicating its satisfaction with the offer);
670 /// * we give up finding a suitable entry because it already took "too long"; or
671 /// * we have offered all entries.
673 Ipc::StoreMap::visitVictims(const NameFilter visitor
)
675 // Hopefully, we find a usable entry much sooner (TODO: use time?).
676 // The min() will protect us from division by zero inside the loop.
677 const int searchLimit
= min(10000, entryLimit());
679 for (; tries
< searchLimit
; ++tries
) {
680 const sfileno name
= static_cast<sfileno
>(++anchors
->victim
% entryLimit());
685 debugs(54, 5, "no victims found in " << path
<< "; tried: " << tries
);
690 Ipc::StoreMap::purgeOne()
692 return visitVictims([&](const sfileno name
) {
693 const sfileno fileno
= fileNoByName(name
);
694 Anchor
&s
= anchorAt(fileno
);
695 if (s
.lock
.lockExclusive()) {
696 // the caller wants a free slice; empty anchor is not enough
697 if (!s
.empty() && s
.start
>= 0) {
698 // this entry may be marked for deletion, and that is OK
699 freeChain(fileno
, s
, false);
700 debugs(54, 5, "purged entry " << fileno
<< " from " << path
);
703 s
.lock
.unlockExclusive();
710 Ipc::StoreMap::importSlice(const SliceId sliceId
, const Slice
&slice
)
712 // Slices are imported into positions that should not be available via
713 // "get free slice" API. This is not something we can double check
714 // reliably because the anchor for the imported slice may not have been
716 assert(validSlice(sliceId
));
717 sliceAt(sliceId
) = slice
;
721 Ipc::StoreMap::entryLimit() const
723 return min(sliceLimit(), static_cast<int>(SwapFilenMax
+1));
727 Ipc::StoreMap::entryCount() const
729 return anchors
->count
;
733 Ipc::StoreMap::sliceLimit() const
735 return slices
->capacity
;
739 Ipc::StoreMap::updateStats(ReadWriteLockStats
&stats
) const
741 for (int i
= 0; i
< anchors
->capacity
; ++i
)
742 anchorAt(i
).lock
.updateStats(stats
);
746 Ipc::StoreMap::validEntry(const int pos
) const
748 return 0 <= pos
&& pos
< entryLimit();
752 Ipc::StoreMap::validSlice(const int pos
) const
754 return 0 <= pos
&& pos
< sliceLimit();
757 Ipc::StoreMap::Anchor
&
758 Ipc::StoreMap::anchorAt(const sfileno fileno
)
760 assert(validEntry(fileno
));
761 return anchors
->items
[fileno
];
764 const Ipc::StoreMap::Anchor
&
765 Ipc::StoreMap::anchorAt(const sfileno fileno
) const
767 return const_cast<StoreMap
&>(*this).anchorAt(fileno
);
771 Ipc::StoreMap::nameByKey(const cache_key
*const key
) const
774 const uint64_t *const k
= reinterpret_cast<const uint64_t *>(key
);
775 // TODO: use a better hash function
776 const int hash
= (k
[0] + k
[1]) % entryLimit();
781 Ipc::StoreMap::fileNoByName(const sfileno name
) const
783 // fileNos->items are initialized to zero, which we treat as "name is fileno";
784 // a positive value means the entry anchor got moved to a new fileNo
785 if (const int item
= fileNos
->items
[name
])
790 /// map `name` to `fileNo`
792 Ipc::StoreMap::relocate(const sfileno name
, const sfileno fileno
)
794 // preserve special meaning for zero; see fileNoByName
795 fileNos
->items
[name
] = fileno
+1;
799 Ipc::StoreMap::fileNoByKey(const cache_key
*const key
) const
801 const int name
= nameByKey(key
);
802 return fileNoByName(name
);
805 Ipc::StoreMap::Anchor
&
806 Ipc::StoreMap::anchorByKey(const cache_key
*const key
)
808 return anchorAt(fileNoByKey(key
));
811 Ipc::StoreMap::Slice
&
812 Ipc::StoreMap::sliceAt(const SliceId sliceId
)
814 assert(validSlice(sliceId
));
815 return slices
->items
[sliceId
];
818 const Ipc::StoreMap::Slice
&
819 Ipc::StoreMap::sliceAt(const SliceId sliceId
) const
821 return const_cast<StoreMap
&>(*this).sliceAt(sliceId
);
824 /* Ipc::StoreMapAnchor */
826 Ipc::StoreMapAnchor::StoreMapAnchor(): start(0), splicingPoint(-1)
828 // keep in sync with rewind()
832 Ipc::StoreMapAnchor::setKey(const cache_key
*const aKey
)
834 memcpy(key
, aKey
, sizeof(key
));
835 waitingToBeFreed
= Store::Root().markedForDeletion(aKey
);
839 Ipc::StoreMapAnchor::sameKey(const cache_key
*const aKey
) const
841 const uint64_t *const k
= reinterpret_cast<const uint64_t *>(aKey
);
842 return k
[0] == key
[0] && k
[1] == key
[1];
846 Ipc::StoreMapAnchor::set(const StoreEntry
&from
, const cache_key
*aKey
)
848 assert(writing() && !reading());
849 setKey(reinterpret_cast<const cache_key
*>(aKey
? aKey
: from
.key
));
850 basics
.timestamp
= from
.timestamp
;
851 basics
.lastref
= from
.lastref
;
852 basics
.expires
= from
.expires
;
853 basics
.lastmod
= from
.lastModified();
854 basics
.swap_file_sz
= from
.swap_file_sz
;
855 basics
.refcount
= from
.refcount
;
857 // do not copy key bit if we are not using from.key
858 // TODO: Replace KEY_PRIVATE with a nil StoreEntry::key!
859 uint16_t cleanFlags
= from
.flags
;
861 EBIT_CLR(cleanFlags
, KEY_PRIVATE
);
862 basics
.flags
= cleanFlags
;
866 Ipc::StoreMapAnchor::exportInto(StoreEntry
&into
) const
869 into
.timestamp
= basics
.timestamp
;
870 into
.lastref
= basics
.lastref
;
871 into
.expires
= basics
.expires
;
872 into
.lastModified(basics
.lastmod
);
873 into
.swap_file_sz
= basics
.swap_file_sz
;
874 into
.refcount
= basics
.refcount
;
875 const bool collapsingRequired
= into
.hittingRequiresCollapsing();
876 into
.flags
= basics
.flags
;
877 // There are possibly several flags we do not need to overwrite,
878 // and ENTRY_REQUIRES_COLLAPSING is one of them.
879 // TODO: check for other flags.
880 into
.setCollapsingRequirement(collapsingRequired
);
884 Ipc::StoreMapAnchor::rewind()
889 memset(&key
, 0, sizeof(key
));
891 waitingToBeFreed
= false;
892 writerHalted
= false;
896 /* Ipc::StoreMapUpdate */
898 Ipc::StoreMapUpdate::StoreMapUpdate(StoreEntry
*anEntry
):
901 entry
->lock("Ipc::StoreMapUpdate1");
904 Ipc::StoreMapUpdate::StoreMapUpdate(const StoreMapUpdate
&other
):
909 entry
->lock("Ipc::StoreMapUpdate2");
912 Ipc::StoreMapUpdate::~StoreMapUpdate()
914 entry
->unlock("Ipc::StoreMapUpdate");
917 /* Ipc::StoreMap::Owner */
919 Ipc::StoreMap::Owner::Owner():
926 Ipc::StoreMap::Owner::~Owner()
933 /* Ipc::StoreMapAnchors */
935 Ipc::StoreMapAnchors::StoreMapAnchors(const int aCapacity
):
944 Ipc::StoreMapAnchors::sharedMemorySize() const
946 return SharedMemorySize(capacity
);
950 Ipc::StoreMapAnchors::SharedMemorySize(const int capacity
)
952 return sizeof(StoreMapAnchors
) + capacity
* sizeof(StoreMapAnchor
);