]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Initial implementation of atomic container for free page numbers in a segment.
authorAlex Rousskov <rousskov@measurement-factory.com>
Thu, 31 Mar 2011 20:43:33 +0000 (14:43 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Thu, 31 Mar 2011 20:43:33 +0000 (14:43 -0600)
src/ipc/Makefile.am
src/ipc/mem/PageStack.cc [new file with mode: 0644]
src/ipc/mem/PageStack.h [new file with mode: 0644]

index da53ddb4f0f50d6cc960de9e45ef85f37dcdfbe2..d1bca5272ece70b7b06b87325b84ed8856a3bebd 100644 (file)
@@ -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 (file)
index 0000000..113c636
--- /dev/null
@@ -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<Shared *>(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 (file)
index 0000000..6cc1842
--- /dev/null
@@ -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<Offset> theSize;
+
+        /// last readable item index; just a hint, not a guarantee
+        AtomicWordT<Offset> theLastReadable;
+        /// first writable item index; just a hint, not a guarantee
+        AtomicWordT<Offset> theFirstWritable;
+
+        typedef AtomicWordT<Value> 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