#include "tools.h"
/// shared memory segment path to use for MemStore maps
-static const char *MapLabel = "cache_mem_map";
+static const SBuf MapLabel("cache_mem_map");
/// shared memory segment path to use for the free slices index
static const char *SpaceLabel = "cache_mem_space";
+/// shared memory segment path to use for IDs of shared pages with slice data
+static const char *ExtrasLabel = "cache_mem_ex";
// TODO: sync with Rock::SwapDir::*Path()
// We store free slot IDs (i.e., "space") as Page objects so that we can use
}
freeSlots = shm_old(Ipc::Mem::PageStack)(SpaceLabel);
+ extras = shm_old(Extras)(ExtrasLabel);
Must(!map);
map = new MemStoreMap(MapLabel);
const size_t prefixSize = e.mem_obj->endOffset() - sliceOffset;
assert(prefixSize <= wasSize);
- const MemStoreMap::Extras &extras = map->extras(sid);
- char *page = static_cast<char*>(PagePointer(extras.page));
+ const MemStoreMapExtras::Item &extra = extras->items[sid];
+
+ char *page = static_cast<char*>(PagePointer(extra.page));
const StoreIOBuffer sliceBuf(wasSize - prefixSize,
e.mem_obj->endOffset(),
page + prefixSize);
if (!copyFromShmSlice(e, sliceBuf, wasEof))
return false;
debugs(20, 9, "entry " << index << " copied slice " << sid <<
- " from " << extras.page << " +" << prefixSize);
+ " from " << extra.page << '+' << prefixSize);
}
// else skip a [possibly incomplete] slice that we copied earlier
if (anchor.start < 0) { // must allocate the very first slot for e
Ipc::Mem::PageId page;
anchor.start = reserveSapForWriting(page); // throws
- map->extras(anchor.start).page = page;
+ extras->items[anchor.start].page = page;
}
lastWritingSlice = anchor.start;
Ipc::Mem::PageId page;
slice.next = lastWritingSlice = reserveSapForWriting(page);
- map->extras(lastWritingSlice).page = page;
+ extras->items[lastWritingSlice].page = page;
debugs(20, 7, "entry " << index << " new slice: " << lastWritingSlice);
}
Ipc::StoreMap::Slice &slice =
map->writeableSlice(e.mem_obj->memCache.index, lastWritingSlice);
- Ipc::Mem::PageId page = map->extras(lastWritingSlice).page;
+ Ipc::Mem::PageId page = extras->items[lastWritingSlice].page;
assert(lastWritingSlice >= 0 && page);
debugs(20, 7, "entry " << e << " slice " << lastWritingSlice << " has " <<
page);
void
MemStore::noteFreeMapSlice(const Ipc::StoreMapSliceId sliceId)
{
- Ipc::Mem::PageId &pageId = map->extras(sliceId).page;
+ Ipc::Mem::PageId &pageId = extras->items[sliceId].page;
debugs(20, 9, "slice " << sliceId << " freed " << pageId);
assert(pageId);
Ipc::Mem::PageId slotId;
{
public:
/* RegisteredRunner API */
- MemStoreRr(): spaceOwner(NULL), mapOwner(NULL) {}
+ MemStoreRr(): spaceOwner(NULL), mapOwner(NULL), extrasOwner(NULL) {}
virtual void finalizeConfig();
virtual void claimMemoryNeeds();
virtual void useConfig();
private:
Ipc::Mem::Owner<Ipc::Mem::PageStack> *spaceOwner; ///< free slices Owner
MemStoreMap::Owner *mapOwner; ///< primary map Owner
+ Ipc::Mem::Owner<MemStoreMapExtras> *extrasOwner; ///< PageIds Owner
};
RunnerRegistrationEntry(MemStoreRr);
entryLimit, 0);
Must(!mapOwner);
mapOwner = MemStoreMap::Init(MapLabel, entryLimit);
+ Must(!extrasOwner);
+ extrasOwner = shm_new(MemStoreMapExtras)(ExtrasLabel, entryLimit);
}
MemStoreRr::~MemStoreRr()
{
+ delete extrasOwner;
delete mapOwner;
delete spaceOwner;
}
#include "Store.h"
// StoreEntry restoration info not already stored by Ipc::StoreMap
-struct MemStoreMapExtras {
+struct MemStoreMapExtraItem {
Ipc::Mem::PageId page; ///< shared memory page with entry slice content
};
-typedef Ipc::StoreMapWithExtras<MemStoreMapExtras> MemStoreMap;
+typedef Ipc::StoreMapItems<MemStoreMapExtraItem> MemStoreMapExtras;
+typedef Ipc::StoreMap MemStoreMap;
/// Stores HTTP entities in RAM. Current implementation uses shared memory.
/// Unlike a disk store (SwapDir), operations are synchronous (and fast).
Ipc::Mem::Pointer<Ipc::Mem::PageStack> freeSlots; ///< unused map slot IDs
MemStoreMap *map; ///< index of mem-cached entries
+ typedef MemStoreMapExtras Extras;
+ Ipc::Mem::Pointer<Extras> extras; ///< IDs of pages with slice data
+
/// the last allocate slice for writing a store entry (during copyToShm)
sfileno lastWritingSlice;
#include <limits>
-/// shared memory segment path to use for Transients maps
-static const char *MapLabel = "transients_map";
+/// shared memory segment path to use for Transients map
+static const SBuf MapLabel("transients_map");
+/// shared memory segment path to use for Transients map extras
+static const char *ExtrasLabel = "transients_ex";
Transients::Transients(): map(NULL), locals(NULL)
{
map = new TransientsMap(MapLabel);
map->cleaner = this;
+ extras = shm_old(TransientsMapExtras)(ExtrasLabel);
+
locals = new Locals(entryLimit, 0);
}
StoreEntry *
Transients::copyFromShm(const sfileno index)
{
- const TransientsMap::Extras &extras = map->extras(index);
+ const TransientsMapExtras::Item &extra = extras->items[index];
// create a brand new store entry and initialize it with stored info
- StoreEntry *e = storeCreatePureEntry(extras.url, extras.url,
- extras.reqFlags, extras.reqMethod);
+ StoreEntry *e = storeCreatePureEntry(extra.url, extra.url,
+ extra.reqFlags, extra.reqMethod);
assert(e->mem_obj);
- e->mem_obj->method = extras.reqMethod;
+ e->mem_obj->method = extra.reqMethod;
e->mem_obj->xitTable.io = MemObject::ioReading;
e->mem_obj->xitTable.index = index;
const RequestFlags &reqFlags,
const HttpRequestMethod &reqMethod)
{
- TransientsMap::Extras &extras = map->extras(index);
+ TransientsMapExtras::Item &extra = extras->items[index];
const char *url = e.url();
const size_t urlLen = strlen(url);
- Must(urlLen < sizeof(extras.url)); // we have space to store it all, plus 0
- strncpy(extras.url, url, sizeof(extras.url));
- extras.url[urlLen] = '\0';
+ Must(urlLen < sizeof(extra.url)); // we have space to store it all, plus 0
+ strncpy(extra.url, url, sizeof(extra.url));
+ extra.url[urlLen] = '\0';
- extras.reqFlags = reqFlags;
+ extra.reqFlags = reqFlags;
Must(reqMethod != Http::METHOD_OTHER);
- extras.reqMethod = reqMethod.id();
+ extra.reqMethod = reqMethod.id();
return true;
}
{
public:
/* RegisteredRunner API */
- TransientsRr(): mapOwner(NULL) {}
+ TransientsRr(): mapOwner(NULL), extrasOwner(NULL) {}
virtual void useConfig();
virtual ~TransientsRr();
private:
TransientsMap::Owner *mapOwner;
+ Ipc::Mem::Owner<TransientsMapExtras> *extrasOwner;
};
RunnerRegistrationEntry(TransientsRr);
Must(!mapOwner);
mapOwner = TransientsMap::Init(MapLabel, entryLimit);
+ Must(!extrasOwner);
+ extrasOwner = shm_new(TransientsMapExtras)(ExtrasLabel, entryLimit);
}
TransientsRr::~TransientsRr()
{
+ delete extrasOwner;
delete mapOwner;
}
#include <vector>
// StoreEntry restoration info not already stored by Ipc::StoreMap
-struct TransientsMapExtras {
+struct TransientsMapExtraItem {
char url[MAX_URL+1]; ///< Request-URI; TODO: decrease MAX_URL by one
RequestFlags reqFlags; ///< request flags
Http::MethodType reqMethod; ///< request method; extensions are not supported
};
-typedef Ipc::StoreMapWithExtras<TransientsMapExtras> TransientsMap;
+typedef Ipc::StoreMapItems<TransientsMapExtraItem> TransientsMapExtras;
+typedef Ipc::StoreMap TransientsMap;
/// Keeps track of store entries being delivered to clients that arrived before
/// those entries were [fully] cached. This shared table is necessary to sync
/// shared packed info indexed by Store keys, for creating new StoreEntries
TransientsMap *map;
+ /// shared packed info that standard StoreMap does not store for us
+ typedef TransientsMapExtras Extras;
+ Ipc::Mem::Pointer<Extras> extras;
+
typedef std::vector<StoreEntry*> Locals;
/// local collapsed entries indexed by transient ID, for syncing old StoreEntries
Locals *locals;
}
-const char *
+SBuf
Rock::SwapDir::inodeMapPath() const
{
- static String inodesPath;
- inodesPath = path;
- inodesPath.append("_inodes");
- return inodesPath.termedBuf();
+ return Ipc::Mem::Segment::Name(SBuf(path), "map");
}
const char *
virtual void parse(int index, char *path);
// temporary path to the shared memory map of first slots of cached entries
- const char *inodeMapPath() const;
+ SBuf inodeMapPath() const;
// temporary path to the shared memory stack of free slots
const char *freeSlotsPath() const;
#include "squid.h"
#include "ipc/StoreMap.h"
+#include "SBuf.h"
#include "Store.h"
#include "store_key_md5.h"
#include "tools.h"
-Ipc::StoreMap::Owner *
-Ipc::StoreMap::Init(const char *const path, const int slotLimit, const size_t extrasSize)
+static SBuf
+StoreMapSlicesId(const SBuf &path)
{
- assert(slotLimit > 0); // we should not be created otherwise
- Owner *const owner = shm_new(Shared)(path, slotLimit, extrasSize);
- debugs(54, 5, HERE << "new map [" << path << "] created: " << slotLimit);
- return owner;
+ return Ipc::Mem::Segment::Name(path, "slices");
}
-Ipc::StoreMap::Owner *
-Ipc::StoreMap::Init(const char *const path, const int slotLimit)
+static SBuf
+StoreMapAnchorsId(const SBuf &path)
{
- return Init(path, slotLimit, 0);
+ return Ipc::Mem::Segment::Name(path, "anchors");
}
-Ipc::StoreMap::StoreMap(const char *const aPath): cleaner(NULL), path(aPath),
- shared(shm_old(Shared)(aPath))
+Ipc::StoreMap::Owner *
+Ipc::StoreMap::Init(const SBuf &path, const int sliceLimit)
+{
+ assert(sliceLimit > 0); // we should not be created otherwise
+ const int anchorLimit = min(sliceLimit, static_cast<int>(SwapFilenMax));
+ Owner *owner = new Owner;
+ owner->anchors = shm_new(Anchors)(StoreMapAnchorsId(path).c_str(), anchorLimit);
+ owner->slices = shm_new(Slices)(StoreMapSlicesId(path).c_str(), sliceLimit);
+ debugs(54, 5, "created " << path << " with " << anchorLimit << '+' << sliceLimit);
+ return owner;
+}
+
+Ipc::StoreMap::StoreMap(const SBuf &aPath): cleaner(NULL), path(aPath),
+ anchors(shm_old(Anchors)(StoreMapAnchorsId(path).c_str())),
+ slices(shm_old(Slices)(StoreMapSlicesId(path).c_str()))
{
- assert(shared->slotLimit > 0); // we should not be created otherwise
- debugs(54, 5, HERE << "attached map [" << path << "] created: " <<
- shared->slotLimit);
+ debugs(54, 5, "attached " << path << " with " <<
+ anchors->capacity << '+' << slices->capacity);
+ assert(entryLimit() > 0); // key-to-position mapping requires this
+ assert(entryLimit() <= sliceLimit()); // at least one slice per entry
}
int
Ipc::StoreMap::compareVersions(const sfileno fileno, time_t newVersion) const
{
- assert(validEntry(fileno));
- Anchor &inode = shared->slots[fileno].anchor;
+ const Anchor &inode = anchorAt(fileno);
// note: we do not lock, so comparison may be inacurate
void
Ipc::StoreMap::forgetWritingEntry(sfileno fileno)
{
- assert(validEntry(fileno));
- Anchor &inode = shared->slots[fileno].anchor;
+ Anchor &inode = anchorAt(fileno);
assert(inode.writing());
inode.rewind();
inode.lock.unlockExclusive();
- --shared->entryCount;
+ anchors->count;
debugs(54, 8, "closed entry " << fileno << " for writing " << path);
}
Ipc::StoreMap::Anchor *
Ipc::StoreMap::openForWritingAt(const sfileno fileno, bool overwriteExisting)
{
- Anchor &s = shared->slots[fileno].anchor;
+ Anchor &s = anchorAt(fileno);
ReadWriteLock &lock = s.lock;
if (lock.lockExclusive()) {
assert(s.empty());
s.start = -1; // we have not allocated any slices yet
- ++shared->entryCount;
+ ++anchors->count;
//s.setKey(key); // XXX: the caller should do that
debugs(54, 5, "opened entry " << fileno << " for writing " << path);
void
Ipc::StoreMap::startAppending(const sfileno fileno)
{
- assert(validEntry(fileno));
- Anchor &s = shared->slots[fileno].anchor;
+ Anchor &s = anchorAt(fileno);
assert(s.writing());
s.lock.startAppending();
debugs(54, 5, "restricted entry " << fileno << " to appending " << path);
void
Ipc::StoreMap::closeForWriting(const sfileno fileno, bool lockForReading)
{
- assert(validEntry(fileno));
- Anchor &s = shared->slots[fileno].anchor;
+ Anchor &s = anchorAt(fileno);
assert(s.writing());
if (lockForReading) {
s.lock.switchExclusiveToShared();
Ipc::StoreMap::Slice &
Ipc::StoreMap::writeableSlice(const AnchorId anchorId, const SliceId sliceId)
{
- assert(validEntry(anchorId));
- assert(shared->slots[anchorId].anchor.writing());
+ assert(anchorAt(anchorId).writing());
assert(validSlice(sliceId));
- return shared->slots[sliceId].slice;
+ return sliceAt(sliceId);
}
const Ipc::StoreMap::Slice &
Ipc::StoreMap::readableSlice(const AnchorId anchorId, const SliceId sliceId) const
{
- assert(validEntry(anchorId));
- assert(shared->slots[anchorId].anchor.reading());
+ assert(anchorAt(anchorId).reading());
assert(validSlice(sliceId));
- return shared->slots[sliceId].slice;
+ return sliceAt(sliceId);
}
Ipc::StoreMap::Anchor &
Ipc::StoreMap::writeableEntry(const AnchorId anchorId)
{
- assert(validEntry(anchorId));
- assert(shared->slots[anchorId].anchor.writing());
- return shared->slots[anchorId].anchor;
+ assert(anchorAt(anchorId).writing());
+ return anchorAt(anchorId);
}
const Ipc::StoreMap::Anchor &
Ipc::StoreMap::readableEntry(const AnchorId anchorId) const
{
- assert(validEntry(anchorId));
- assert(shared->slots[anchorId].anchor.reading());
- return shared->slots[anchorId].anchor;
+ assert(anchorAt(anchorId).reading());
+ return anchorAt(anchorId);
}
void
Ipc::StoreMap::abortWriting(const sfileno fileno)
{
debugs(54, 5, "aborting entry " << fileno << " for writing " << path);
- assert(validEntry(fileno));
- Anchor &s = shared->slots[fileno].anchor;
+ Anchor &s = anchorAt(fileno);
assert(s.writing());
s.lock.appending = false; // locks out any new readers
if (!s.lock.readers) {
const Ipc::StoreMap::Anchor *
Ipc::StoreMap::peekAtReader(const sfileno fileno) const
{
- assert(validEntry(fileno));
- const Anchor &s = shared->slots[fileno].anchor;
+ const Anchor &s = anchorAt(fileno);
if (s.reading())
return &s; // immediate access by lock holder so no locking
if (s.writing())
const Ipc::StoreMap::Anchor &
Ipc::StoreMap::peekAtEntry(const sfileno fileno) const
{
- assert(validEntry(fileno));
- return shared->slots[fileno].anchor;
+ return anchorAt(fileno);
}
void
{
debugs(54, 5, "marking entry " << fileno << " to be freed in " << path);
- assert(validEntry(fileno));
- Anchor &s = shared->slots[fileno].anchor;
+ Anchor &s = anchorAt(fileno);
if (s.lock.lockExclusive())
freeChain(fileno, s, false);
<< " to be freed in " << path);
const int idx = anchorIndexByKey(key);
- Anchor &s = shared->slots[idx].anchor;
+ Anchor &s = anchorAt(idx);
if (s.lock.lockExclusive()) {
if (s.sameKey(key))
freeChain(idx, s, true);
sfileno sliceId = inode.start;
debugs(54, 8, "first slice " << sliceId);
while (sliceId >= 0) {
- Slice &slice = shared->slots[sliceId].slice;
+ Slice &slice = sliceAt(sliceId);
const sfileno nextId = slice.next;
slice.size = 0;
slice.next = -1;
if (!keepLocked)
inode.lock.unlockExclusive();
- --shared->entryCount;
+ --anchors->count;
debugs(54, 5, "freed entry " << fileno << " in " << path);
}
Ipc::StoreMap::openForReadingAt(const sfileno fileno)
{
debugs(54, 5, "opening entry " << fileno << " for reading " << path);
- assert(validEntry(fileno));
- Anchor &s = shared->slots[fileno].anchor;
+ Anchor &s = anchorAt(fileno);
if (!s.lock.lockShared()) {
debugs(54, 5, "cannot open busy entry " << fileno <<
void
Ipc::StoreMap::closeForReading(const sfileno fileno)
{
- assert(validEntry(fileno));
- Anchor &s = shared->slots[fileno].anchor;
+ Anchor &s = anchorAt(fileno);
assert(s.reading());
s.lock.unlockShared();
debugs(54, 5, "closed entry " << fileno << " for reading " << path);
const int searchLimit = min(10000, entryLimit());
int tries = 0;
for (; tries < searchLimit; ++tries) {
- const sfileno fileno = static_cast<sfileno>(++shared->victim % entryLimit());
- assert(validEntry(fileno));
- Anchor &s = shared->slots[fileno].anchor;
+ const sfileno fileno = static_cast<sfileno>(++anchors->victim % entryLimit());
+ Anchor &s = anchorAt(fileno);
if (s.lock.lockExclusive()) {
// the caller wants a free slice; empty anchor is not enough
if (!s.empty() && s.start >= 0) {
// reliably because the anchor for the imported slice may not have been
// imported yet.
assert(validSlice(sliceId));
- shared->slots[sliceId].slice = slice;
+ sliceAt(sliceId) = slice;
}
int
int
Ipc::StoreMap::entryCount() const
{
- return shared->entryCount;
+ return anchors->count;
}
int
Ipc::StoreMap::sliceLimit() const
{
- return shared->slotLimit;
+ return slices->capacity;
}
void
Ipc::StoreMap::updateStats(ReadWriteLockStats &stats) const
{
- for (int i = 0; i < shared->slotLimit; ++i)
- shared->slots[i].anchor.lock.updateStats(stats);
+ for (int i = 0; i < anchors->capacity; ++i)
+ anchorAt(i).lock.updateStats(stats);
}
bool
return 0 <= pos && pos < sliceLimit();
}
+Ipc::StoreMap::Anchor&
+Ipc::StoreMap::anchorAt(const sfileno fileno) {
+ assert(validEntry(fileno));
+ return anchors->items[fileno];
+}
+
+const Ipc::StoreMap::Anchor&
+Ipc::StoreMap::anchorAt(const sfileno fileno) const {
+ return const_cast<StoreMap&>(*this).anchorAt(fileno);
+}
+
sfileno
Ipc::StoreMap::anchorIndexByKey(const cache_key *const key) const
{
Ipc::StoreMap::Anchor &
Ipc::StoreMap::anchorByKey(const cache_key *const key)
{
- return shared->slots[anchorIndexByKey(key)].anchor;
+ return anchorAt(anchorIndexByKey(key));
+}
+
+Ipc::StoreMap::Slice&
+Ipc::StoreMap::sliceAt(const SliceId sliceId) {
+ assert(validSlice(sliceId));
+ return slices->items[sliceId];
+}
+
+const Ipc::StoreMap::Slice&
+Ipc::StoreMap::sliceAt(const SliceId sliceId) const {
+ return const_cast<StoreMap&>(*this).sliceAt(sliceId);
}
/* Ipc::StoreMapAnchor */
// but keep the lock
}
-/* Ipc::StoreMap::Shared */
+Ipc::StoreMap::Owner::Owner(): anchors(NULL), slices(NULL)
+{
+}
+
+Ipc::StoreMap::Owner::~Owner()
+{
+ delete anchors;
+ delete slices;
+}
+
+/* Ipc::StoreMapAnchors */
-Ipc::StoreMap::Shared::Shared(const int aSlotLimit, const size_t anExtrasSize):
- slotLimit(aSlotLimit), extrasSize(anExtrasSize), entryCount(0),
+Ipc::StoreMapAnchors::StoreMapAnchors(const int aCapacity):
+ count(0),
victim(0),
- slots(aSlotLimit)
+ capacity(aCapacity),
+ items(aCapacity)
{
}
size_t
-Ipc::StoreMap::Shared::sharedMemorySize() const
+Ipc::StoreMapAnchors::sharedMemorySize() const
{
- return SharedMemorySize(slotLimit, extrasSize);
+ return SharedMemorySize(capacity);
}
size_t
-Ipc::StoreMap::Shared::SharedMemorySize(const int slotLimit, const size_t extrasSize)
+Ipc::StoreMapAnchors::SharedMemorySize(const int capacity)
{
- return sizeof(Shared) + slotLimit * (sizeof(StoreMapSlot) + extrasSize);
+ return sizeof(StoreMapAnchors) + capacity * sizeof(StoreMapAnchor);
}
#include "ipc/mem/FlexibleArray.h"
#include "ipc/mem/Pointer.h"
#include "ipc/ReadWriteLock.h"
+#include "SBuf.h"
#include "typedefs.h"
namespace Ipc
/// where the chain of StoreEntry slices begins [app]
Atomic::WordT<StoreMapSliceId> start;
+};
+
+/// an array of shareable Items
+/// must be the last data member or, if used as a parent class, the last parent
+template <class C>
+class StoreMapItems
+{
+public:
+ typedef C Item;
+ typedef Ipc::Mem::Owner< StoreMapItems<Item> > Owner;
+
+ explicit StoreMapItems(const int aCapacity): capacity(aCapacity), items(aCapacity) {}
-#if 0
- /// possible persistent states
- typedef enum {
- Empty, ///< ready for writing, with nothing of value
- Writeable, ///< transitions from Empty to Readable
- Readable, ///< ready for reading
- } State;
- State state; ///< current state
-#endif
+ size_t sharedMemorySize() const { return SharedMemorySize(capacity); }
+ static size_t SharedMemorySize(const int aCapacity) { return sizeof(StoreMapItems<Item>) + aCapacity*sizeof(Item); }
+
+ const int capacity; ///< total number of items
+ Ipc::Mem::FlexibleArray<Item> items; ///< storage
};
-/// A hack to allocate one shared array for both anchors and slices.
-/// Anchors are indexed by store entry ID and are independent from each other.
-/// Slices are indexed by slice IDs and form entry chains using slice.next.
-class StoreMapSlot
+/// StoreMapSlices indexed by their slice ID.
+typedef StoreMapItems<StoreMapSlice> StoreMapSlices;
+
+/// StoreMapAnchors indexed by entry fileno plus
+/// sharing-safe basic housekeeping info about Store entries
+class StoreMapAnchors
{
public:
- StoreMapAnchor anchor; ///< information about store entry as a whole
- StoreMapSlice slice; ///< information about one stored entry piece
+ typedef Ipc::Mem::Owner< StoreMapAnchors > Owner;
+
+ explicit StoreMapAnchors(const int aCapacity);
+
+ size_t sharedMemorySize() const;
+ static size_t SharedMemorySize(const int anAnchorLimit);
+
+ Atomic::Word count; ///< current number of entries
+ Atomic::WordT<uint32_t> victim; ///< starting point for purge search
+ const int capacity; ///< total number of anchors
+ Ipc::Mem::FlexibleArray<StoreMapAnchor> items; ///< anchors storage
};
+// TODO: Find an elegant way to use StoreMapItems in StoreMapAnchors
class StoreMapCleaner;
-/// map of StoreMapSlots indexed by their keys, with read/write slice locking
-/// kids extend to store custom data
+/// Manages shared Store index (e.g., locking/unlocking/freeing entries) using
+/// StoreMapAnchors indexed by their keys and
+/// StoreMapSlices indexed by their slide ID.
class StoreMap
{
public:
typedef StoreMapAnchor Anchor;
+ typedef StoreMapAnchors Anchors;
typedef sfileno AnchorId;
typedef StoreMapSlice Slice;
+ typedef StoreMapSlices Slices;
typedef StoreMapSliceId SliceId;
- /// data shared across maps in different processes
- class Shared
- {
+public:
+ /// aggregates anchor and slice owners for Init() caller convenience
+ class Owner {
public:
- Shared(const int aSlotLimit, const size_t anExtrasSize);
- size_t sharedMemorySize() const;
- static size_t SharedMemorySize(const int aSlotLimit, const size_t anExtrasSize);
-
- const int slotLimit; ///< total number of slots
- const size_t extrasSize; ///< size of extra data in each slot
- Atomic::Word entryCount; ///< current number of entries
- Atomic::WordT<uint32_t> victim; ///< starting point for purge search
- Ipc::Mem::FlexibleArray<StoreMapSlot> slots; ///< storage
+ Owner();
+ ~Owner();
+ Anchors::Owner *anchors;
+ Slices::Owner *slices;
+ private:
+ Owner(const Owner &); // not implemented
+ Owner &operator =(const Owner &); // not implemented
};
-public:
- typedef Mem::Owner<Shared> Owner;
-
/// initialize shared memory
- static Owner *Init(const char *const path, const int slotLimit);
+ static Owner *Init(const SBuf &path, const int slotLimit);
- StoreMap(const char *const aPath);
+ StoreMap(const SBuf &aPath);
/// computes map entry position for a given entry key
sfileno anchorIndexByKey(const cache_key *const key) const;
StoreMapCleaner *cleaner; ///< notified before a readable entry is freed
protected:
- static Owner *Init(const char *const path, const int limit, const size_t extrasSize);
-
- const String path; ///< cache_dir path or similar cache name; for logging
- Mem::Pointer<Shared> shared;
+ const SBuf path; ///< cache_dir path or similar cache name; for logging
+ Mem::Pointer<StoreMapAnchors> anchors; ///< entry inodes (starting blocks)
+ Mem::Pointer<StoreMapSlices> slices; ///< chained entry pieces positions
private:
+ Anchor &anchorAt(const sfileno fileno);
+ const Anchor &anchorAt(const sfileno fileno) const;
Anchor &anchorByKey(const cache_key *const key);
+ Slice &sliceAt(const SliceId sliceId);
+ const Slice &sliceAt(const SliceId sliceId) const;
Anchor *openForReading(Slice &s);
void freeChain(const sfileno fileno, Anchor &inode, const bool keepLock);
};
-/// StoreMap with extra slice data
-/// Note: ExtrasT must be POD, it is initialized with zeroes, no
-/// constructors or destructors are called
-template <class ExtrasT>
-class StoreMapWithExtras: public StoreMap
-{
-public:
- typedef ExtrasT Extras;
-
- /// initialize shared memory
- static Owner *Init(const char *const path, const int limit);
-
- StoreMapWithExtras(const char *const path);
-
- /// write access to the extras; call openForWriting() first!
- ExtrasT &extras(const StoreMapSliceId sid);
- /// read-only access to the extras; call openForReading() first!
- const ExtrasT &extras(const StoreMapSliceId sid) const;
-
-protected:
-
- ExtrasT *sharedExtras; ///< pointer to extras in shared memory
-};
-
/// API for adjusting external state when dirty map slice is being freed
class StoreMapCleaner
{
virtual void noteFreeMapSlice(const StoreMapSliceId sliceId) = 0;
};
-// StoreMapWithExtras implementation
-
-template <class ExtrasT>
-StoreMap::Owner *
-StoreMapWithExtras<ExtrasT>::Init(const char *const path, const int limit)
-{
- return StoreMap::Init(path, limit, sizeof(Extras));
-}
-
-template <class ExtrasT>
-StoreMapWithExtras<ExtrasT>::StoreMapWithExtras(const char *const aPath):
- StoreMap(aPath)
-{
- const size_t sharedSizeWithoutExtras =
- Shared::SharedMemorySize(sliceLimit(), 0);
- sharedExtras = reinterpret_cast<Extras *>(reinterpret_cast<char *>(shared.getRaw()) + sharedSizeWithoutExtras);
-}
-
-template <class ExtrasT>
-ExtrasT &
-StoreMapWithExtras<ExtrasT>::extras(const StoreMapSliceId sid)
-{
- return const_cast<ExtrasT &>(const_cast<const StoreMapWithExtras *>(this)->extras(sid));
-}
-
-template <class ExtrasT>
-const ExtrasT &
-StoreMapWithExtras<ExtrasT>::extras(const StoreMapSliceId sid) const
-{
- assert(sharedExtras);
- assert(validSlice(sid));
- return sharedExtras[sid];
-}
-
} // namespace Ipc
// We do not reuse FileMap because we cannot control its size,
#include "Debug.h"
#include "fatal.h"
#include "ipc/mem/Segment.h"
+#include "SBuf.h"
#include "tools.h"
#include <fcntl.h>
return result;
}
+SBuf
+Ipc::Mem::Segment::Name(const SBuf &prefix, const char *suffix)
+{
+ SBuf result = prefix;
+ result.append("_");
+ result.append(suffix);
+ return result;
+}
+
#if HAVE_SHM
Ipc::Mem::Segment::Segment(const char *const id):
#include "base/RunnersRegistry.h"
#include "SquidString.h"
+class SBuf;
+
namespace Ipc
{
/// common path of all segment names in path-based environments
static const char *BasePath;
+ /// concatenates parts of a name to form a complete name (or its prefix)
+ static SBuf Name(const SBuf &prefix, const char *suffix);
+
private:
// not implemented