shared = reinterpret_cast<Shared *>(shm.mem());
}
+bool
+Rock::DirMap::initialize(const cache_key *const key, const StoreEntryBasics &seBasics)
+{
+ Slot &s = slot(key);
+ if (s.state.swap_if(Slot::WaitingToBeInitialized, Slot::Initializing)) {
+ assert(!s.readLevel);
+ s.setKey(key);
+ s.seBasics = seBasics;
+ ++shared->count;
+ assert(s.state.swap_if(Slot::Initializing, Slot::Usable));
+ return true;
+ }
+ return false;
+}
+
+bool
+Rock::DirMap::initialize(const int idx)
+{
+ return valid(idx) &&
+ shared->slots[idx].state.swap_if(Slot::WaitingToBeInitialized, Slot::Empty);
+}
+
+StoreEntryBasics *
+Rock::DirMap::add(const cache_key *const key)
+{
+ Slot &s = slot(key);
+ if (s.state.swap_if(Slot::Empty, Slot::Writing)) {
+ assert(!s.readLevel);
+ s.setKey(key);
+ return &s.seBasics;
+ }
+ return 0;
+}
+
+void
+Rock::DirMap::added(const cache_key *const key)
+{
+ Slot &s = slot(key);
+ assert(s.checkKey(key));
+ assert(s.state == Slot::Writing);
+ assert(!s.readLevel);
+ ++shared->count;
+ assert(s.state.swap_if(Slot::Writing, Slot::Usable));
+}
+
+bool
+Rock::DirMap::free(const cache_key *const key)
+{
+ if (open(key)) {
+ slot(key).state.swap_if(Slot::Usable, Slot::WaitingToBeFreed);
+ close(key);
+ }
+ return false;
+}
+
+const StoreEntryBasics *
+Rock::DirMap::open(const cache_key *const key)
+{
+ Slot &s = slot(key);
+ if (s.checkKey(key) && s.state == Slot::Usable) {
+ ++s.readLevel;
+ if (s.checkKey(key) && s.state == Slot::Usable)
+ return &s.seBasics;
+ --s.readLevel;
+ freeIfNeeded(s);
+ }
+ return 0;
+}
+
+void
+Rock::DirMap::close(const cache_key *const key)
+{
+ Slot &s = slot(key);
+ assert(s.checkKey(key));
+ assert(s.readLevel > 0);
+ --s.readLevel;
+ freeIfNeeded(s);
+}
+
int
Rock::DirMap::entryLimit() const
{
}
int
-Rock::DirMap::useNext()
+Rock::DirMap::AbsoluteEntryLimit()
{
- assert(!full());
- const int next = findNext();
- assert(valid(next)); // because we were not full
- use(next);
- return next;
+ const int sfilenoMax = 0xFFFFFF; // Core sfileno maximum
+ return sfilenoMax;
}
int
-Rock::DirMap::AbsoluteEntryLimit()
+Rock::DirMap::slotIdx(const cache_key *const key) const
{
- const int sfilenoMax = 0xFFFFFF; // Core sfileno maximum
- return sfilenoMax;
+ const uint64_t *const k = reinterpret_cast<const uint64_t *>(&key);
+ // TODO: use a better hash function
+ return (k[0] + k[1]) % shared->limit;
}
-void
-Rock::DirMap::use(const int pos)
+Rock::DirMap::Slot &
+Rock::DirMap::slot(const cache_key *const key)
{
- if (!has(pos)) {
- assert(valid(pos));
- shared->slots[pos] = 1;
- ++shared->count;
- debugs(8, 6, HERE << pos);
- } else {
- debugs(8, 3, HERE << pos << " in vain");
- }
+ return shared->slots[slotIdx(key)];
}
void
-Rock::DirMap::clear(const int pos)
+Rock::DirMap::freeIfNeeded(Slot &s)
{
- if (has(pos)) {
- shared->slots[pos] = 0;
+ if (!s.readLevel &&
+ s.state.swap_if(Slot::WaitingToBeFreed, Slot::Freeing)) {
+ memset(s.key, 0, sizeof(s.key));
+ memset(&s.seBasics, 0, sizeof(s.seBasics));
--shared->count;
- debugs(8, 6, HERE << pos);
- } else {
- debugs(8, 3, HERE << pos << " in vain");
- assert(valid(pos));
+ s.state.swap_if(Slot::Freeing, Slot::Empty);
}
- if (shared->hintPast < 0)
- shared->hintPast = pos; // remember cleared slot
}
-bool
-Rock::DirMap::has(const int pos) const
+int
+Rock::DirMap::SharedSize(const int limit)
{
- if (!valid(pos)) // the only place where we are forgiving
- return false;
-
- return shared->slots[pos];
+ return sizeof(Shared) + limit * sizeof(Slot);
}
-/// low-level empty-slot search routine, uses and updates hints
-int
-Rock::DirMap::findNext() const
-{
- // try the clear-based hint, if any
- if (shared->hintPast >= 0) {
- const int result = shared->hintPast;
- shared->hintPast = -1; // assume used; or we could update it in set()
- if (valid(result) && !has(result))
- return result;
- }
-
- // adjust and try the scan-based hint
- if (!valid(shared->hintNext))
- shared->hintNext = 0;
-
- for (int i = 0; i < shared->limit; ++i) {
- if (!has(shared->hintNext))
- return shared->hintNext++;
-
- shared->hintNext = (shared->hintNext + 1) % shared->limit;
- }
-
- // the map is full
- return -1;
+void
+Rock::DirMap::Slot::setKey(const cache_key *const aKey)
+{
+ memcpy(key, &aKey, sizeof(key));
}
-int
-Rock::DirMap::SharedSize(const int limit)
+bool
+Rock::DirMap::Slot::checkKey(const cache_key *const aKey) const
{
- return sizeof(Shared) + limit * sizeof(Slot);
+ const uint64_t *const k = reinterpret_cast<const uint64_t *>(&key);
+ return k[0] == key[0] && k[1] == key[1];
}
-Rock::DirMap::Shared::Shared(const int aLimit):
- hintPast(-1), hintNext(0), limit(aLimit), count(0)
+Rock::DirMap::Shared::Shared(const int aLimit): limit(aLimit), count(0)
{
}
#include "ipc/AtomicWord.h"
#include "ipc/SharedMemory.h"
+class StoreEntryBasics {
+public:
+ /* START OF ON-DISK STORE_META_STD TLV field */
+ time_t timestamp;
+ time_t lastref;
+ time_t expires;
+ time_t lastmod;
+ uint64_t swap_file_sz;
+ u_short refcount;
+ u_short flags;
+ /* END OF ON-DISK STORE_META_STD */
+};
+
namespace Rock {
/// \ingroup Rock
DirMap(const int id, const int limit); ///< create a new shared DirMap
DirMap(const int id); ///< open an existing shared DirMap
+ /// initialize usable slot
+ bool initialize(const cache_key *const key, const StoreEntryBasics &seBasics);
+ /// initialize empty slot
+ bool initialize(const int idx);
+
+ /// start adding a new entry
+ StoreEntryBasics *add(const cache_key *const key);
+ /// finish adding a new entry
+ void added(const cache_key *const key);
+
+ /// mark slot as waiting to be freed, will be freed when no one uses it
+ bool free(const cache_key *const key);
+
+ /// open slot for reading, increments read level
+ const StoreEntryBasics *open(const cache_key *const key);
+ /// close slot after reading, decrements read level
+ void close(const cache_key *const key);
+
bool full() const; ///< there are no empty slots left
- bool has(int n) const; ///< whether slot n is occupied
bool valid(int n) const; ///< whether n is a valid slot coordinate
int entryCount() const; ///< number of used slots
int entryLimit() const; ///< maximum number of slots that can be used
- void use(int n); ///< mark slot n as used
- void clear(int n); ///< mark slot n as unused
- int useNext(); ///< finds and uses an empty slot, returning its coordinate
-
static int AbsoluteEntryLimit(); ///< maximum entryLimit() possible
private:
- int findNext() const;
-
- static int SharedSize(const int limit);
-
- SharedMemory shm; ///< shared memory segment
+ struct Slot {
+ enum {
+ WaitingToBeInitialized,
+ Initializing,
+ Empty,
+ Writing,
+ Usable,
+ WaitingToBeFreed,
+ Freeing
+ };
+
+ void setKey(const cache_key *const aKey);
+ bool checkKey(const cache_key *const aKey) const;
+
+ AtomicWordT<uint8_t> state; ///< slot state
+ AtomicWord readLevel; ///< read level
+ AtomicWordT<uint64_t> key[2]; ///< MD5 entry key
+ StoreEntryBasics seBasics; ///< basic store entry data
+ };
- typedef AtomicWordT<uint8_t> Slot;
struct Shared {
Shared(const int aLimit);
- /// unreliable next empty slot suggestion #1 (clear based)
- mutable AtomicWord hintPast;
- ///< unreliable next empty slot suggestion #2 (scan based)
- mutable AtomicWord hintNext;
-
- AtomicWord limit; ///< maximum number of map slots
+ const AtomicWord limit; ///< maximum number of map slots
AtomicWord count; ///< current number of map slots
Slot slots[]; ///< slots storage
};
+
+ int slotIdx(const cache_key *const key) const;
+ Slot &slot(const cache_key *const key);
+ void freeIfNeeded(Slot &s);
+
+ static int SharedSize(const int limit);
+
+ SharedMemory shm; ///< shared memory segment
Shared *shared; ///< pointer to shared memory
};