benchmark silo added
[c11concurrency-benchmarks.git] / silo / pxqueue.h
diff --git a/silo/pxqueue.h b/silo/pxqueue.h
new file mode 100644 (file)
index 0000000..8c68739
--- /dev/null
@@ -0,0 +1,354 @@
+#pragma once
+
+#include <iterator>
+#include <string>
+
+#include "counter.h"
+#include "macros.h"
+#include "util.h"
+
+// abstract queue for RCU-like queues
+
+// forward decl
+template <typename T, size_t N> struct basic_px_queue;
+
+template <typename T, size_t N>
+struct basic_px_group {
+  basic_px_group()
+    : next_(nullptr), rcu_tick_(0)
+  {
+    static event_counter evt_px_group_creates(
+        util::cxx_typename<T>::value() + std::string("_px_group_creates"));
+    ++evt_px_group_creates;
+  }
+  ~basic_px_group()
+  {
+    static event_counter evt_px_group_deletes(
+        util::cxx_typename<T>::value() + std::string("_px_group_deletes"));
+    ++evt_px_group_deletes;
+  }
+
+  // no copying/moving
+  basic_px_group(const basic_px_group &) = delete;
+  basic_px_group &operator=(const basic_px_group &) = delete;
+  basic_px_group(basic_px_group &&) = delete;
+
+  static const size_t GroupSize = N;
+  friend class basic_px_queue<T, N>;
+
+private:
+  basic_px_group *next_;
+  typename util::vec<T, GroupSize>::type pxs_;
+  uint64_t rcu_tick_; // all elements in pxs_ are from this tick,
+                      // this number is only meaningful if pxs_ is
+                      // not empty
+};
+
+// not thread safe- should guard with lock for concurrent manipulation
+template <typename T, size_t N>
+struct basic_px_queue {
+  basic_px_queue()
+    : head_(nullptr), tail_(nullptr),
+      freelist_head_(nullptr), freelist_tail_(nullptr),
+      ngroups_(0) {}
+
+  typedef basic_px_group<T, N> px_group;
+
+  basic_px_queue(basic_px_queue &&) = default;
+  basic_px_queue(const basic_px_queue &) = delete;
+  basic_px_queue &operator=(const basic_px_queue &) = delete;
+
+  ~basic_px_queue()
+  {
+    reap_chain(head_);
+    reap_chain(freelist_head_);
+  }
+
+  void
+  swap(basic_px_queue &other)
+  {
+    std::swap(head_, other.head_);
+    std::swap(tail_, other.tail_);
+    std::swap(freelist_head_, other.freelist_head_);
+    std::swap(freelist_tail_, other.freelist_tail_);
+    std::swap(ngroups_, other.ngroups_);
+  }
+
+  template <typename PtrType, typename ObjType>
+  class iterator_ : public std::iterator<std::forward_iterator_tag, ObjType> {
+  public:
+    inline iterator_() : px_(nullptr), i_() {}
+    inline iterator_(PtrType *px) : px_(px), i_() {}
+
+    // allow iterator to assign to const_iterator
+    template <typename P, typename O>
+    inline iterator_(const iterator_<P, O> &o) : px_(o.px_), i_(o.i_) {}
+
+    inline ObjType &
+    operator*() const
+    {
+      return px_->pxs_[i_];
+    }
+
+    inline ObjType *
+    operator->() const
+    {
+      return &px_->pxs_[i_];
+    }
+
+    inline uint64_t
+    tick() const
+    {
+      return px_->rcu_tick_;
+    }
+
+    inline bool
+    operator==(const iterator_ &o) const
+    {
+      return px_ == o.px_ && i_ == o.i_;
+    }
+
+    inline bool
+    operator!=(const iterator_ &o) const
+    {
+      return !operator==(o);
+    }
+
+    inline iterator_ &
+    operator++()
+    {
+      ++i_;
+      if (i_ == px_->pxs_.size()) {
+        px_ = px_->next_;
+        i_ = 0;
+      }
+      return *this;
+    }
+
+    inline iterator_
+    operator++(int)
+    {
+      iterator_ cur = *this;
+      ++(*this);
+      return cur;
+    }
+
+  private:
+    PtrType *px_;
+    size_t i_;
+  };
+
+  typedef iterator_<px_group, T> iterator;
+  typedef iterator_<const px_group, const T> const_iterator;
+
+  inline iterator begin() { return iterator(head_); }
+  inline const_iterator begin() const { return iterator(head_); }
+
+  inline iterator end() { return iterator(nullptr); }
+  inline const_iterator end() const { return iterator(nullptr); }
+
+  // enqueue t in epoch rcu_tick
+  // assumption: rcu_ticks can only go up!
+  void
+  enqueue(const T &t, uint64_t rcu_tick)
+  {
+    INVARIANT(bool(head_) == bool(tail_));
+    INVARIANT(bool(head_) == bool(ngroups_));
+    INVARIANT(!tail_ || tail_->pxs_.size() <= px_group::GroupSize);
+    INVARIANT(!tail_ || tail_->rcu_tick_ <= rcu_tick);
+    px_group *g;
+    if (unlikely(!tail_ ||
+                 tail_->pxs_.size() == px_group::GroupSize ||
+                 tail_->rcu_tick_ != rcu_tick)) {
+      ensure_freelist();
+      // pop off freelist
+      g = freelist_head_;
+      freelist_head_ = g->next_;
+      if (g == freelist_tail_)
+        freelist_tail_ = nullptr;
+      g->next_ = nullptr;
+      g->pxs_.clear();
+      g->rcu_tick_ = rcu_tick;
+      ngroups_++;
+
+      // adjust ptrs
+      if (!head_) {
+        head_ = tail_ = g;
+      } else {
+        tail_->next_ = g;
+        tail_ = g;
+      }
+    } else {
+      g = tail_;
+    }
+    INVARIANT(g->pxs_.size() < px_group::GroupSize);
+    INVARIANT(g->rcu_tick_ == rcu_tick);
+    INVARIANT(!g->next_);
+    INVARIANT(tail_ == g);
+    g->pxs_.emplace_back(t);
+    sanity_check();
+  }
+
+  void
+  ensure_freelist()
+  {
+    INVARIANT(bool(freelist_head_) == bool(freelist_tail_));
+    if (likely(freelist_head_))
+      return;
+    const size_t nalloc = 16;
+    alloc_freelist(nalloc);
+  }
+
+  inline bool
+  empty() const
+  {
+    return !head_;
+  }
+
+#ifdef CHECK_INVARIANTS
+  void
+  sanity_check() const
+  {
+    INVARIANT(bool(head_) == bool(tail_));
+    INVARIANT(!tail_ || ngroups_);
+    INVARIANT(!tail_ || !tail_->next_);
+    INVARIANT(bool(freelist_head_) == bool(freelist_tail_));
+    INVARIANT(!freelist_tail_ || !freelist_tail_->next_);
+    px_group *p = head_, *pprev = nullptr;
+    size_t n = 0;
+    uint64_t prev_tick = 0;
+    while (p) {
+      INVARIANT(p->pxs_.size());
+      INVARIANT(p->pxs_.size() <= px_group::GroupSize);
+      INVARIANT(prev_tick <= p->rcu_tick_);
+      prev_tick = p->rcu_tick_;
+      pprev = p;
+      p = p->next_;
+      n++;
+    }
+    INVARIANT(n == ngroups_);
+    INVARIANT(!pprev || tail_ == pprev);
+    p = freelist_head_;
+    pprev = nullptr;
+    while (p) {
+      pprev = p;
+      p = p->next_;
+    }
+    INVARIANT(!pprev || freelist_tail_ == pprev);
+  }
+#else
+  inline ALWAYS_INLINE void sanity_check() const {}
+#endif
+
+  // assumes this instance is EMPTY, accept from source
+  // all entries <= e
+  inline void
+  empty_accept_from(basic_px_queue &source, uint64_t rcu_tick)
+  {
+    ALWAYS_ASSERT(empty());
+    INVARIANT(this != &source);
+    INVARIANT(!tail_);
+    px_group *p = source.head_, *pnext;
+    while (p && p->rcu_tick_ <= rcu_tick) {
+      pnext = p->next_;
+      p->next_ = nullptr;
+      if (!head_) {
+        head_ = tail_ = p;
+      } else {
+        tail_->next_ = p;
+        tail_ = p;
+      }
+      ngroups_++;
+      source.ngroups_--;
+      source.head_ = p = pnext;
+      if (!source.head_)
+        source.tail_ = nullptr;
+    }
+    sanity_check();
+    source.sanity_check();
+  }
+
+  // transfer *this* elements freelist to dest
+  void
+  transfer_freelist(basic_px_queue &dest, ssize_t n = -1)
+  {
+    if (!freelist_head_)
+      return;
+    if (n < 0) {
+      freelist_tail_->next_ = dest.freelist_head_;
+      if (!dest.freelist_tail_)
+        dest.freelist_tail_ = freelist_tail_;
+      dest.freelist_head_ = freelist_head_;
+      freelist_head_ = freelist_tail_ = nullptr;
+    } else {
+      px_group *p = freelist_head_;
+      size_t c = 0;
+      while (p && c++ < static_cast<size_t>(n)) {
+        px_group *tmp = p->next_;
+        p->next_ = dest.freelist_head_;
+        dest.freelist_head_ = p;
+        if (!dest.freelist_tail_)
+          dest.freelist_tail_ = p;
+        if (p == freelist_tail_)
+          freelist_tail_ = nullptr;
+        p = tmp;
+        freelist_head_ = p;
+      }
+    }
+    sanity_check();
+  }
+
+  void
+  clear()
+  {
+    if (!head_)
+      return;
+    tail_->next_ = freelist_head_;
+    if (!freelist_tail_)
+      freelist_tail_ = tail_;
+    freelist_head_ = head_;
+    head_ = tail_ = nullptr;
+    ngroups_ = 0;
+  }
+
+  // adds n new groups to the freelist
+  void
+  alloc_freelist(size_t n)
+  {
+    for (size_t i = 0; i < n; i++) {
+      px_group *p = new px_group;
+      if (!freelist_tail_)
+        freelist_tail_ = p;
+      p->next_ = freelist_head_;
+      freelist_head_ = p;
+    }
+  }
+
+  inline size_t get_ngroups() const { return ngroups_; }
+
+  inline bool
+  get_latest_epoch(uint64_t &e) const
+  {
+    if (!tail_)
+      return false;
+    e = tail_->rcu_tick_;
+    return true;
+  }
+
+private:
+  void
+  reap_chain(px_group *px)
+  {
+    while (px) {
+      px_group *tmp = px->next_;
+      delete px;
+      px = tmp;
+    }
+  }
+
+  px_group *head_;
+  px_group *tail_;
+  px_group *freelist_head_;
+  px_group *freelist_tail_;
+  size_t ngroups_;
+};