From: Alex Rousskov Date: Thu, 31 Mar 2011 20:43:33 +0000 (-0600) Subject: Initial implementation of atomic container for free page numbers in a segment. X-Git-Tag: take06~54 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=be17aa829fb87c6a8ca80cbe9329698e8c3ecc19;p=thirdparty%2Fsquid.git Initial implementation of atomic container for free page numbers in a segment. --- diff --git a/src/ipc/Makefile.am b/src/ipc/Makefile.am index da53ddb4f0..d1bca5272e 100644 --- a/src/ipc/Makefile.am +++ b/src/ipc/Makefile.am @@ -36,6 +36,9 @@ libipc_la_SOURCES = \ Strand.cc \ Strand.h \ \ - forward.h + forward.h \ + \ + mem/PageStack.cc \ + mem/PageStack.h DEFS += -DDEFAULT_PREFIX=\"$(prefix)\" diff --git a/src/ipc/mem/PageStack.cc b/src/ipc/mem/PageStack.cc new file mode 100644 index 0000000000..113c636021 --- /dev/null +++ b/src/ipc/mem/PageStack.cc @@ -0,0 +1,95 @@ +/* + * $Id$ + * + * DEBUG: section 54 Interprocess Communication + * + */ + +#include "config.h" + +#include "base/TextException.h" +#include "ipc/mem/PageStack.h" + +/// used to mark a stack slot available for storing free page offsets +const Ipc::Mem::PageStack::Value Writable = 0; + + +Ipc::Mem::PageStack::PageStack(const String &id, const unsigned int capacity): + shm(id.termedBuf()) +{ + shm.create(sizeof(Shared::Item) * capacity + sizeof(Shared)); + assert(shm.mem()); + shared = new (shm.mem()) Shared(capacity); +} + +Ipc::Mem::PageStack::PageStack(const String &id): shm(id.termedBuf()) +{ + shm.open(); + shared = reinterpret_cast(shm.mem()); + assert(shared); +} + +/* + * TODO: We currently rely on the theLastReadable hint during each + * loop iteration. We could also use hint just for the start position: + * (const Offset start = theLastReadable) and then scan the stack + * sequentially regardless of theLastReadable changes by others. Which + * approach is better? Same for push(). + */ +bool +Ipc::Mem::PageStack::pop(Value &value) +{ + // we may fail to dequeue, but be conservative to prevent long searches + --shared->theSize; + + // find a Readable slot, starting with theLastReadable and going left + while (shared->theSize >= 0) { + const Offset idx = shared->theLastReadable; + // mark the slot at ids Writable while extracting its current value + value = shared->theItems[idx].fetchAndAnd(0); // works if Writable is 0 + const bool popped = value != Writable; + // theItems[idx] is probably not Readable [any more] + // help others (if we popped) or go left ourselves (if we did not) + shared->theLastReadable.swap_if(idx, shared->prev(idx)); // may fail or lie + if (popped) { + // the slot we emptied may already be filled, but that is OK + shared->theFirstWritable = idx; // may lie + return true; + } + // TODO: report suspiciously long loops + } + + ++shared->theSize; + return false; +} + +void +Ipc::Mem::PageStack::push(const Value value) +{ + Must(value != Writable); + // find a Writable slot, starting with theFirstWritable and going right + while (shared->theSize < shared->theCapacity) { + const Offset idx = shared->theFirstWritable; + const bool pushed = shared->theItems[idx].swap_if(Writable, value); + // theItems[idx] is probably not Writable [any more]; + // help others (if we pushed) or go right ourselves (if we did not) + shared->theFirstWritable.swap_if(idx, shared->next(idx)); // may fail or lie + if (pushed) { + // the enqueued value may already by gone, but that is OK + shared->theLastReadable = idx; // may lie + ++shared->theSize; + return; + } + // TODO: report suspiciously long loops + } + Must(false); // the number of pages cannot exceed theCapacity +} + +Ipc::Mem::PageStack::Shared::Shared(const unsigned int aCapacity): + theCapacity(aCapacity), theSize(theCapacity), + theLastReadable(prev(theSize)), theFirstWritable(next(theLastReadable)) +{ + // initially, all pages are free + for (Offset i = 0; i < theSize; ++i) + theItems[i] = i + 1; // skip page number zero to keep numbers positive +} diff --git a/src/ipc/mem/PageStack.h b/src/ipc/mem/PageStack.h new file mode 100644 index 0000000000..6cc1842c90 --- /dev/null +++ b/src/ipc/mem/PageStack.h @@ -0,0 +1,64 @@ +/* + * $Id$ + * + */ + +#ifndef SQUID_IPC_MEM_PAGE_STACK_H +#define SQUID_IPC_MEM_PAGE_STACK_H + +#include "ipc/AtomicWord.h" +#include "ipc/SharedMemory.h" + +namespace Ipc { + +namespace Mem { + +/// Atomic container of "free" page numbers inside a single SharedMemory space. +/// Assumptions: all page numbers are unique, positive, have an known maximum, +/// and can be temporary unavailable as long as they are never trully lost. +class PageStack { +public: + typedef uint32_t Value; ///< stack item type (a free page number) + + /// creates a new shared stack that can hold up to capacity items + PageStack(const String &id, const unsigned int capacity); + /// attaches to the identified shared stack + PageStack(const String &id); + + /// sets value and returns true unless no free page numbers are found + bool pop(Value &value); + /// makes value available as a free page number to future pop() callers + void push(const Value value); + +private: + typedef unsigned int Offset; ///< stack index type + + struct Shared { + Shared(const unsigned int theCapacity); + + // these help iterate the stack in search of a free spot or a page + Offset next(const Offset idx) const { return (idx + 1) % theCapacity; } + Offset prev(const Offset idx) const { return (theCapacity + idx - 1) % theCapacity; } + + const Offset theCapacity; ///< stack capacity, i.e. theItems size + /// lower bound for the number of free pages (may get negative!) + AtomicWordT theSize; + + /// last readable item index; just a hint, not a guarantee + AtomicWordT theLastReadable; + /// first writable item index; just a hint, not a guarantee + AtomicWordT theFirstWritable; + + typedef AtomicWordT Item; + Item theItems[]; ///< page number storage + }; + + SharedMemory shm; ///< shared memory segment to store metadata (and pages) + Shared *shared; ///< our metadata, shared among all stack users +}; + +} // namespace Mem + +} // namespace Ipc + +#endif // SQUID_IPC_MEM_PAGE_STACK_H