From ee292aa558dbc536586b0396dd5db1353b70e8d4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Tomic=CC=8Cevic=CC=81?=
 <dominik.tomicevic@gmail.com>
Date: Mon, 7 Dec 2015 21:50:07 +0100
Subject: [PATCH] implemented correct skiplist

---
 data_structures/skiplist/skiplist.hpp | 511 ++++++++++++++++++++------
 examples/skiplist                     | Bin 11792 -> 0 bytes
 examples/skiplist.cpp                 |  45 ++-
 memory/deferred_recycler.hpp          |  29 ++
 memory/lazy_gc.hpp                    |   2 +
 memory/recycler.hpp                   |  19 +-
 utils/auto_scope.hpp                  |   2 +-
 7 files changed, 484 insertions(+), 124 deletions(-)
 delete mode 100755 examples/skiplist
 create mode 100644 memory/deferred_recycler.hpp

diff --git a/data_structures/skiplist/skiplist.hpp b/data_structures/skiplist/skiplist.hpp
index 573a1da99..61047c5ff 100644
--- a/data_structures/skiplist/skiplist.hpp
+++ b/data_structures/skiplist/skiplist.hpp
@@ -2,22 +2,26 @@
 
 #include <algorithm>
 #include <memory>
+#include <cassert>
 
-#include "threading/hazard_ptr.hpp"
 #include "threading/sync/lockable.hpp"
 #include "threading/sync/spinlock.hpp"
 
 #include "utils/random/fast_binomial.hpp"
+#include "memory/lazy_gc.hpp"
 
 // concurrent skiplist based on the implementation described in
 // "A Provably Correct Scalable Concurrent Skip List"
 // https://www.cs.tau.ac.il/~shanir/nir-pubs-web/Papers/OPODIS2006-BA.pdf
 
 template <class K, class T, size_t H=32, class lock_t=SpinLock>
-class SkipList
+class SkipList : LazyGC<SkipList<K, T, H, lock_t>>, Lockable<lock_t>
 {
+public:
     static thread_local FastBinomial<H> rnd;
 
+    using data_t = std::pair<const K, T>;
+
     struct Flags
     {
         enum node_flags : uint8_t {
@@ -51,62 +55,20 @@ class SkipList
 
     class Node : Lockable<lock_t>
     {
+    public:
         friend class SkipList;
 
-    public:
-        class reference
-        {
-        public:
-            reference() {}
-
-            reference(Node* node, bool store_hazard = true)
-                : node(node), hazard(store_hazard ? node : nullptr) {}
-
-            reference(const reference&) = delete;
-
-            reference(reference&& other)
-                : node(other.node), hazard(std::move(other.hazard))
-            {
-                other.node = nullptr;
-            }
-
-            reference& operator=(const reference& other) = delete;
-
-            reference& operator=(const reference&& other)
-            {
-                node = other.node;
-                other.node = nullptr;
-                hazard = std::move(other.hazard);
-
-                return *this;
-            }
-
-            T& operator*() { return *node; }
-            const T& operator*() const { return *node; }
-
-            T* operator->() { return node; }
-            const T* operator->() const { return node; }
-
-            operator T*() { return node; }
-            const operator T*() const { return node; }
-
-            const hazard_ptr& get_hazard() const
-            {
-                return hazard;
-            }
-
-        private:
-            hazard_ptr hazard;
-            Node* node;
-        };
-
-        K key;
-        T item;
+        data_t data;
 
         const uint8_t height;
         Flags flags;
 
-        static Node* create(K key, T item, uint8_t height)
+        static Node* create(const K& key, const T& item, uint8_t height)
+        {
+            return create({key, item}, height);
+        }
+
+        static Node* create(data_t&& data, uint8_t height)
         {
             // [      Node      ][Node*][Node*][Node*]...[Node*]
             //         |            |      |      |         |
@@ -120,7 +82,7 @@ class SkipList
 
             // we have raw memory and we need to construct an object
             // of type Node on it
-            return new (node) Node(key, item, height);
+            return new (node) Node(std::move(data), height);
         }
 
         static void destroy(Node* node)
@@ -129,20 +91,19 @@ class SkipList
             free(node);
         }
 
-        Node::reference forward(size_t level) const
+        Node* forward(size_t level) const
         {
-            while(true)
-            {
-                auto ref = Node::reference(load_tower(level));
+            return tower[level].load(std::memory_order_acquire);
+        }
 
-                if(ref.get_hazard() == load_tower(level))
-                    return std::move(ref);
-            }
+        void forward(size_t level, Node* next)
+        {
+            tower[level].store(next, std::memory_order_release);
         }
 
     private:
-        Node(K key, T item, uint8_t height)
-            : key(key), item(item), height(height)
+        Node(data_t data, uint8_t height)
+            : data(data), height(height)
         {
             // here we assume, that the memory for N towers (N = height) has
             // been allocated right after the Node structure so we need to
@@ -157,16 +118,6 @@ class SkipList
                 tower[i].~atomic();
         }
 
-        void forward(size_t level, Node* next)
-        {
-            tower[level].store(next, std::memory_order_release);
-        }
-
-        Node* load_tower(size_t level)
-        {
-            return tower[level].load(std::memory_order_acquire);
-        }
-
         // this creates an array of the size zero. we can't put any sensible
         // value here since we don't know what size it will be untill the
         // node is allocated. we could make it a Node** but then we would
@@ -176,52 +127,254 @@ class SkipList
         std::atomic<Node*> tower[0];
     };
 
-    using node_ref_t = typename Node::reference;
-
 public:
-    class iterator
+    class ConstIterator
     {
+        friend class SkipList;
+        ConstIterator(Node* node) : node(node) {}
+
     public:
-        iterator() = default;
-        iterator(node_ref_t&& node) : node(std::move(node)) {}
-        iterator(const iterator&) = delete;
+        ConstIterator() = default;
+        ConstIterator(const ConstIterator&) = default;
 
-        T& operator*() { return node; }
-        T* operator->() { return node; }
-
-        operator T*() { return node; }
-
-        iterator& operator++()
+        const data_t& operator*()
         {
+            assert(node != nullptr);
+            return node->data;
+        }
+
+        const data_t* operator->()
+        {
+            assert(node != nullptr);
+            return &node->data;
+        }
+
+        operator const data_t()
+        {
+            assert(node != nullptr);
+            return node->data;
+        }
+
+        ConstIterator& operator++()
+        {
+            assert(node != nullptr);
             node = node->forward(0);
             return *this;
         }
 
-        iterator& operator++(int)
+        ConstIterator& operator++(int)
         {
             return operator++();
         }
 
+        friend bool operator==(const ConstIterator& a, const ConstIterator& b)
+        {
+            return a.node == b.node;
+        }
+
+        friend bool operator!=(const ConstIterator& a, const ConstIterator& b)
+        {
+            return !(a == b);
+        }
+
     private:
-        node_ref_t node;
+        Node* node {nullptr};
+    };
+
+    class Iterator
+    {
+        friend class SkipList;
+        Iterator(Node* node) : node(node) {}
+
+    public:
+        Iterator() = default;
+        Iterator(const Iterator&) = default;
+
+        data_t& operator*()
+        {
+            assert(node != nullptr);
+            return node->data;
+        }
+
+        data_t* operator->()
+        {
+            assert(node != nullptr);
+            return &node->data;
+        }
+
+        operator data_t()
+        {
+            assert(node != nullptr);
+            return node->data;
+        }
+
+        Iterator& operator++()
+        {
+            assert(node != nullptr);
+            node = node->forward(0);
+            return *this;
+        }
+
+        Iterator& operator++(int)
+        {
+            return operator++();
+        }
+
+        friend bool operator==(const Iterator& a, const Iterator& b)
+        {
+            return a.node == b.node;
+        }
+
+        friend bool operator!=(const Iterator& a, const Iterator& b)
+        {
+            return !(a == b);
+        }
+
+    private:
+        Node* node {nullptr};
     };
 
     SkipList() : header(Node::create(K(), T(), H)) {}
 
-    auto begin() { return iterator(header->forward(0)); }
-    auto end() { return iterator(); }
+    friend class Accessor;
+
+    class Accessor
+    {
+        friend class SkipList;
+
+        Accessor(SkipList& skiplist) : skiplist(skiplist) {}
+    public:
+        Accessor(const Accessor&) = delete;
+        Accessor(Accessor&&) = default;
+
+        Iterator begin()
+        {
+            return skiplist.begin();
+        }
+
+        ConstIterator begin() const
+        {
+            return skiplist.cbegin();
+        }
+
+        ConstIterator cbegin() const
+        {
+            return skiplist.cbegin();
+        }
+
+        Iterator end()
+        {
+            return skiplist.end();
+        }
+
+        ConstIterator end() const
+        {
+            return skiplist.cend();
+        }
+
+        ConstIterator cend() const
+        {
+            return skiplist.cend();
+        }
+
+        std::pair<Iterator, bool> insert_unique(const K& key, const T& item)
+        {
+            return skiplist.insert({key, item}, preds, succs);
+        }
+
+        std::pair<Iterator, bool> insert_unique(const K& key, T&& item)
+        {
+            return skiplist.insert({key, std::move(item)}, preds, succs);
+        }
+
+        ConstIterator find(const K& key) const
+        {
+            return skiplist.find(key);
+        }
+
+        Iterator find(const K& key)
+        {
+            return skiplist.find(key);
+        }
+
+        bool remove(const K& key)
+        {
+            return skiplist.remove(key, preds, succs);
+        }
+
+    private:
+        SkipList& skiplist;
+        Node* preds[H], *succs[H];
+    };
+
+    Accessor access()
+    {
+        return Accessor(*this);
+    }
+
+private:
+    using guard_t = std::unique_lock<lock_t>;
+
+    Iterator begin()
+    {
+        return Iterator(header->forward(0));
+    }
+
+    ConstIterator begin() const
+    {
+        return ConstIterator(header->forward(0));
+    }
+
+    ConstIterator cbegin() const
+    {
+        return ConstIterator(header->forward(0));
+    }
+
+    Iterator end()
+    {
+        return Iterator();
+    }
+
+    ConstIterator end() const
+    {
+        return ConstIterator();
+    }
+
+    ConstIterator cend() const
+    {
+        return ConstIterator();
+    }
 
     size_t size() const
     {
         return count.load(std::memory_order_acquire);
     }
 
-    iterator find(const K& key)
+    bool greater(const K& key, const Node* const node)
     {
-        auto pred = node_ref_t(header);
-        node_ref_t node;
+        return node && key > node->data.first;
+    }
 
-        uint8_t h = pred->height - 1;
+    bool less(const K& key, const Node* const node)
+    {
+        return (node == nullptr) || key < node->data.first;
+    }
+
+    ConstIterator find(const K& key) const
+    {
+        return find_node<ConstIterator>(key);
+    }
+
+    Iterator find(const K& key)
+    {
+        return find_node<Iterator>(key);
+    }
+
+    template <class It>
+    It find_node(const K& key)
+    {
+        Node* node, *pred = header;
+        int h = static_cast<int>(pred->height) - 1;
 
         while(true)
         {
@@ -230,7 +383,7 @@ public:
 
             // if we overshoot at every layer, item doesn't exist
             if(h < 0)
-                return iterator();
+                return It();
 
             // the item is farther to the right, continue going right as long
             // as the key is greater than the current node's key
@@ -238,47 +391,169 @@ public:
                 pred = node, node = node->forward(h);
 
             // check if we have a hit. if not, we need to descend down again
-            if(!less(key, node))
-                return iterator(std::move(node));
+            if(!less(key, node) && !node->flags.is_marked())
+                return It(node);
         }
     }
 
-private:
-    std::atomic<size_t> count {0};
-    Node* header;
-
-    bool greater(const K& key, const Node* const node)
+    int find_path(Node* from, int start, const K& key,
+                  Node* preds[], Node* succs[])
     {
-        return node && key > node->key;
-    }
-
-    bool less(const K& key, const Node* const node)
-    {
-        return (node == nullptr) || key < node->key;
-    }
-
-    int find_path(Node& from, int start, const K& key,
-                  node_ref_t (preds&)[],
-                  node_ref_t (succs&)[])
-    {
-        int lfound = -1;
+        int level_found = -1;
         Node* pred = from;
 
-        for(int level = start_level; level >= 0; --level)
+        for(int level = start; level >= 0; --level)
         {
             Node* node = pred->forward(level);
 
             while(greater(key, node))
                 pred = node, node = pred->forward(level);
 
-
-            if(lfound == -1 && !less(key, node))
-                lfound = level;
+            if(level_found == -1 && !less(key, node))
+                level_found = level;
 
             preds[level] = pred;
             succs[level] = node;
         }
 
-        return lfound;
+        return level_found;
     }
+
+    template <bool ADDING>
+    bool lock_nodes(uint8_t height, guard_t guards[],
+                    Node* preds[], Node* succs[])
+    {
+        Node *prepred, *pred, *succ = nullptr;
+        bool valid = true;
+
+        for(int level = 0; valid && level < height; ++level)
+        {
+            pred = preds[level], succ = succs[level];
+
+            if(pred != prepred)
+                guards[level] = pred->acquire_unique(), prepred = pred;
+
+            valid = !pred->flags.is_marked() && pred->forward(level) == succ;
+
+            if(ADDING)
+                valid = valid && (succ == nullptr || !succ->flags.is_marked());
+        }
+
+        return valid;
+    }
+
+    std::pair<Iterator, bool>
+        insert(data_t&& data, Node* preds[], Node* succs[])
+    {
+        while(true)
+        {
+            auto level = find_path(header, H - 1, data.first, preds, succs);
+
+            if(level != -1)
+            {
+                auto found = succs[level];
+
+                if(found->flags.is_marked())
+                    continue;
+
+                while(!found->flags.is_fully_linked())
+                    usleep(250);
+
+                return {Iterator {succs[level]}, false};
+            }
+
+            auto height = rnd();
+            guard_t guards[H];
+
+            // try to acquire the locks for predecessors up to the height of
+            // the new node. release the locks and try again if someone else
+            // has the locks
+            if(!lock_nodes<true>(height, guards, preds, succs))
+                continue;
+
+            // you have the locks, create a new node
+            auto new_node = Node::create(std::move(data), height);
+
+            // link the predecessors and successors, e.g.
+            //
+            // 4 HEAD ... P ------------------------> S ... NULL
+            // 3 HEAD ... ... P -----> NEW ---------> S ... NULL
+            // 2 HEAD ... ... P -----> NEW -----> S ... ... NULL
+            // 1 HEAD ... ... ... P -> NEW -> S ... ... ... NULL
+            for(uint8_t level = 0; level < height; ++level)
+            {
+                new_node->forward(level, succs[level]);
+                preds[level]->forward(level, new_node);
+            }
+
+            new_node->flags.set_fully_linked();
+            count.fetch_add(1, std::memory_order_relaxed);
+
+            return {Iterator {new_node}, true};
+        }
+    }
+
+    bool ok_delete(Node* node, int level)
+    {
+        return node->flags.is_fully_linked()
+            && node->height - 1 == level
+            && !node->flags.is_marked();
+    }
+
+    bool remove(const K& key, Node* preds[], Node* succs[])
+    {
+        Node* node = nullptr;
+        guard_t node_guard;
+        bool marked = false;
+        int height = 0;
+
+        while(true)
+        {
+            auto level = find_path(header, H - 1, key, preds, succs);
+
+            if(!marked && (level == -1 || !ok_delete(succs[level], level)))
+                    return false;
+
+            if(!marked)
+            {
+                node = succs[level];
+                height = node->height;
+                node_guard = node->acquire_unique();
+
+                if(node->flags.is_marked())
+                    return false;
+
+                node->flags.set_marked();
+            }
+
+            guard_t guards[H];
+
+            if(!lock_nodes<false>(height, guards, preds, succs))
+                continue;
+
+            for(int level = height - 1; level >= 0; --level)
+                preds[level]->forward(level, node->forward(level));
+
+            // TODO recycle(node)
+            count.fetch_sub(1, std::memory_order_relaxed);
+            return true;
+        }
+    }
+
+    guard_t gc_lock_acquire()
+    {
+        return this->acquire_unique();
+    }
+
+    void vacuum()
+    {
+
+    }
+
+    std::atomic<size_t> count {0};
+    Node* header;
 };
+
+template <class K, class T, size_t H, class lock_t>
+thread_local FastBinomial<H> SkipList<K, T, H, lock_t>::rnd;
+
diff --git a/examples/skiplist b/examples/skiplist
deleted file mode 100755
index dc0c8c70b19e5dc59eb2051e696eb2bd6df8ce60..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 11792
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
PKzWNh-a?CUD^&asYb5q6

diff --git a/examples/skiplist.cpp b/examples/skiplist.cpp
index 601b9ec2b..062dfce7c 100644
--- a/examples/skiplist.cpp
+++ b/examples/skiplist.cpp
@@ -2,9 +2,52 @@
 
 #include "data_structures/skiplist/skiplist.hpp"
 
+using std::cout;
+using std::endl;
+
+using skiplist_t = SkipList<int, int>;
+
+void print_skiplist(const skiplist_t::Accessor& skiplist)
+{
+    cout << "---- skiplist now has: ";
+
+    for(auto& kv : skiplist)
+        cout << "(" << kv.first << ", " << kv.second << ") ";
+
+    cout << "----" << endl;
+}
+
 int main(void)
 {
-    auto skiplist = new SkipList<int, int>();
+    cout << std::boolalpha;
+    skiplist_t skiplist;
+
+    auto accessor = skiplist.access();
+
+    cout << "added non-existing (1, 10)? (true) "
+         << accessor.insert_unique(1, 10).second << endl;
+
+    cout << "added already existing (1, 10)? (false) "
+         << accessor.insert_unique(1, 10).second << endl;
+
+    accessor.insert_unique(2, 20);
+    print_skiplist(accessor);
+
+    cout << "value at key 3 exists? (false) "
+         << (accessor.find(3) == accessor.end()) << endl;
+
+    cout << "value at key 2 exists? (true) "
+         << (accessor.find(2) != accessor.end()) << endl;
+
+    cout << "at key 2 is? (20) " << accessor.find(2)->second << endl;
+
+    cout << "removed existing (1)? (true) " << accessor.remove(1) << endl;
+    cout << "removed non-existing (3)? (false) " << accessor.remove(3) << endl;
+
+    accessor.insert_unique(1, 10);
+    accessor.insert_unique(4, 40);
+
+    print_skiplist(accessor);
 
     return 0;
 }
diff --git a/memory/deferred_recycler.hpp b/memory/deferred_recycler.hpp
new file mode 100644
index 000000000..f8b3c4b8d
--- /dev/null
+++ b/memory/deferred_recycler.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "recycler.hpp"
+
+template <class T, class Allocator>
+class DeferredRecycler : Recycler<T, Allocator>
+{
+public:
+    using Recycler<T, Allocator>::acquire;
+
+    void recycle(T* item)
+    {
+        auto guard = this->acquire_unique();
+        dirty.push_back(item);
+    }
+
+    void clean()
+    {
+        auto guard = this->acquire_unique();
+
+        for(auto item : dirty)
+            this->recycle_or_delete(item);
+
+        dirty.clear();
+    }
+
+private:
+    std::queue<T*> dirty;
+};
diff --git a/memory/lazy_gc.hpp b/memory/lazy_gc.hpp
index 75f52b9c9..c3a969192 100644
--- a/memory/lazy_gc.hpp
+++ b/memory/lazy_gc.hpp
@@ -33,6 +33,8 @@ public:
             return;
 
         this->derived().vacuum();
+
+        dirty.store(false, std::memory_order_release);
     }
 
 protected:
diff --git a/memory/recycler.hpp b/memory/recycler.hpp
index b9fe797ac..1d4c174b6 100644
--- a/memory/recycler.hpp
+++ b/memory/recycler.hpp
@@ -7,7 +7,7 @@
 #include "threading/sync/spinlock.hpp"
 
 template <class T, class Allocator=std::allocator<T>>
-class Recycler : Lockable<SpinLock>
+class Recycler : public Lockable<SpinLock>
 {
     static constexpr size_t default_max_reserved = 100;
 
@@ -19,17 +19,28 @@ public:
     T* acquire(Args&&... args)
     {
         auto guard = acquire_unique();
-        return new T(std::forward<Args>(args)...); // todo refactor :D
+        return fetch_or_create(std::forward<Args>(args)...);
     }
 
     void release(T* item)
     {
         auto guard = acquire_unique();
-        delete item; // todo refactor :D
+        return recycle_or_delete(item);
     }
 
-private:
+protected:
     Allocator alloc;
     size_t max_reserved {default_max_reserved};
     std::queue<T*> items;
+
+    template <class... Args>
+    T* fetch_or_create(Args&&... args)
+    {
+        return new T(std::forward<Args>(args)...); // todo refactor :D
+    }
+
+    void recycle_or_delete(T* item)
+    {
+        delete item; // todo refactor :D
+    }
 };
diff --git a/utils/auto_scope.hpp b/utils/auto_scope.hpp
index caa7f09d4..bdc71f411 100644
--- a/utils/auto_scope.hpp
+++ b/utils/auto_scope.hpp
@@ -18,7 +18,7 @@
  *  and the app is left in an inconsistent state. ideally, you would like
  *  to call resource.disable regardles of the exception being thrown.
  *  OnScopeExit makes this possible and very convenient via a 'Auto' macro
- *  
+ *
  *  void hard_worker()
  *  {
  *      resource.enable();