X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FConcurrentSkipList.h;h=7d5cb9589c478fa2bcdf916a623d9149bf0fab10;hp=b3a6be251af01947ad57e1ef0bffd846ed5c05dd;hb=4aa405ec8db137b29d403aa204a91ed50aab5bb4;hpb=368a508272851c34011109d674a0fbe7b8b1d5f7 diff --git a/folly/ConcurrentSkipList.h b/folly/ConcurrentSkipList.h index b3a6be25..7d5cb958 100644 --- a/folly/ConcurrentSkipList.h +++ b/folly/ConcurrentSkipList.h @@ -1,5 +1,5 @@ /* - * Copyright 2012 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -117,28 +117,29 @@ Sample usage: } */ -#ifndef FOLLY_CONCURRENT_SKIP_LIST_H_ -#define FOLLY_CONCURRENT_SKIP_LIST_H_ +#pragma once #include -#include -#include -#include -#include #include -#include +#include +#include +#include #include -#include -#include - #include -#include "folly/ConcurrentSkipList-inl.h" -#include "folly/Likely.h" -#include "folly/SmallLocks.h" + +#include +#include +#include +#include namespace folly { -template, int MAX_HEIGHT=24> +template, + // All nodes are allocated using provided SimpleAllocator, + // it should be thread-safe. + typename NodeAlloc = SysAlloc, + int MAX_HEIGHT = 24> class ConcurrentSkipList { // MAX_HEIGHT needs to be at least 2 to suppress compiler // warnings/errors (Werror=uninitialized tiggered due to preds_[1] @@ -146,35 +147,47 @@ class ConcurrentSkipList { static_assert(MAX_HEIGHT >= 2 && MAX_HEIGHT < 64, "MAX_HEIGHT can only be in the range of [2, 64)"); typedef std::unique_lock ScopedLocker; - typedef ConcurrentSkipList SkipListType; + typedef ConcurrentSkipList SkipListType; public: typedef detail::SkipListNode NodeType; typedef T value_type; typedef T key_type; - typedef detail::csl_iterator iterator; typedef detail::csl_iterator const_iterator; class Accessor; class Skipper; - // convenient function to get an Accessor to a new instance. - static Accessor create(int height=1) { - return Accessor(createInstance(height)); + explicit ConcurrentSkipList(int height, const NodeAlloc& alloc) + : recycler_(alloc), + head_(NodeType::create(recycler_.alloc(), height, value_type(), true)), + size_(0) {} + + explicit ConcurrentSkipList(int height) + : recycler_(), + head_(NodeType::create(recycler_.alloc(), height, value_type(), true)), + size_(0) {} + + // Convenient function to get an Accessor to a new instance. + static Accessor create(int height, const NodeAlloc& alloc) { + return Accessor(createInstance(height, alloc)); } - // create a shared_ptr skiplist object with initial head height. - static boost::shared_ptr createInstance(int height=1) { - return boost::shared_ptr(new SkipListType(height)); + static Accessor create(int height = 1) { + return Accessor(createInstance(height)); } - // create a unique_ptr skiplist object with initial head height. - static std::unique_ptr createRawInstance(int height=1) { - return std::unique_ptr(new SkipListType(height)); + // Create a shared_ptr skiplist object with initial head height. + static std::shared_ptr createInstance(int height, + const NodeAlloc& alloc) { + return std::make_shared(height, alloc); } + static std::shared_ptr createInstance(int height = 1) { + return std::make_shared(height); + } //=================================================================== // Below are implementation details. @@ -182,18 +195,18 @@ class ConcurrentSkipList { //=================================================================== ~ConcurrentSkipList() { - LOG_IF(FATAL, recycler_.refs() > 0) - << "number of accessors is not 0, " << recycler_.refs() << " instead!" - << " This shouldn't have happened!"; - while (NodeType* current = head_.load(std::memory_order_relaxed)) { + /* static */ if (NodeType::template DestroyIsNoOp::value) { + // Avoid traversing the list if using arena allocator. + return; + } + for (NodeType* current = head_.load(std::memory_order_relaxed); current; ) { NodeType* tmp = current->skip(0); - NodeType::destroy(current); - head_.store(tmp, std::memory_order_relaxed); + NodeType::destroy(recycler_.alloc(), current); + current = tmp; } } private: - static bool greater(const value_type &data, const NodeType *node) { return node && Comp()(node->data(), data); } @@ -228,87 +241,12 @@ class ConcurrentSkipList { return foundLayer; } - struct Recycler : private boost::noncopyable { - Recycler() : refs_(0), dirty_(false) { lock_.init(); } - - ~Recycler() { - if (nodes_) { - for (auto& node : *nodes_) { - NodeType::destroy(node); - } - } - } - - void add(NodeType* node) { - std::lock_guard g(lock_); - if (nodes_.get() == nullptr) { - nodes_.reset(new std::vector(1, node)); - } else { - nodes_->push_back(node); - } - DCHECK_GT(refs(), 0); - dirty_.store(true, std::memory_order_relaxed); - } - - int refs() const { - return refs_.load(std::memory_order_relaxed); - } - - int addRef() { - return refs_.fetch_add(1, std::memory_order_relaxed); - } - - int release() { - // We don't expect to clean the recycler immediately everytime it is OK - // to do so. Here, it is possible that multiple accessors all release at - // the same time but nobody would clean the recycler here. If this - // happens, the recycler will usually still get cleaned when - // such a race doesn't happen. The worst case is the recycler will - // eventually get deleted along with the skiplist. - if (LIKELY(!dirty_.load(std::memory_order_relaxed) || refs() > 1)) { - return refs_.fetch_add(-1, std::memory_order_relaxed); - } - - boost::scoped_ptr > newNodes; - { - std::lock_guard g(lock_); - if (nodes_.get() == nullptr || refs() > 1) { - return refs_.fetch_add(-1, std::memory_order_relaxed); - } - // once refs_ reaches 1 and there is no other accessor, it is safe to - // remove all the current nodes in the recycler, as we already acquired - // the lock here so no more new nodes can be added, even though new - // accessors may be added after that. - newNodes.swap(nodes_); - dirty_.store(false, std::memory_order_relaxed); - } - - // TODO(xliu) should we spawn a thread to do this when there are large - // number of nodes in the recycler? - for (auto& node : *newNodes) { - NodeType::destroy(node); - } - - // decrease the ref count at the very end, to minimize the - // chance of other threads acquiring lock_ to clear the deleted - // nodes again. - return refs_.fetch_add(-1, std::memory_order_relaxed); - } - - private: - boost::scoped_ptr> nodes_; - std::atomic refs_; // current number of visitors to the list - std::atomic dirty_; // whether *nodes_ is non-empty - MicroSpinLock lock_; // protects access to *nodes_ - }; // class ConcurrentSkipList::Recycler - - explicit ConcurrentSkipList(int height) : - head_(NodeType::create(height, value_type(), true)), size_(0) {} - size_t size() const { return size_.load(std::memory_order_relaxed); } + int height() const { return head_.load(std::memory_order_consume)->height(); } + int maxLayer() const { return height() - 1; } size_t incrementSize(int delta) { @@ -388,10 +326,11 @@ class ConcurrentSkipList { } // locks acquired and all valid, need to modify the links under the locks. - newNode = NodeType::create(nodeHeight, std::forward(data)); - for (int layer = 0; layer < nodeHeight; ++layer) { - newNode->setSkip(layer, succs[layer]); - preds[layer]->setSkip(layer, newNode); + newNode = + NodeType::create(recycler_.alloc(), nodeHeight, std::forward(data)); + for (int k = 0; k < nodeHeight; ++k) { + newNode->setSkip(k, succs[k]); + preds[k]->setSkip(k, newNode); } newNode->setFullyLinked(); @@ -439,8 +378,8 @@ class ConcurrentSkipList { continue; // this will unlock all the locks } - for (int layer = nodeHeight - 1; layer >= 0; --layer) { - preds[layer]->setSkip(layer, nodeToDelete->skip(layer)); + for (int k = nodeHeight - 1; k >= 0; --k) { + preds[k]->setSkip(k, nodeToDelete->skip(k)); } incrementSize(-1); @@ -503,10 +442,11 @@ class ConcurrentSkipList { bool found = false; while (!found) { // stepping down - for (; ht > 0 && less(data, pred->skip(ht - 1)); --ht) {} - if (ht == 0) return std::make_pair(pred->skip(0), 0); // not found + for (; ht > 0 && less(data, node = pred->skip(ht - 1)); --ht) {} + if (ht == 0) return std::make_pair(node, 0); // not found + // node <= data now, but we need to fix up ht + --ht; - node = pred->skip(--ht); // node <= data now // stepping right while (greater(data, node)) { pred = node; @@ -549,7 +489,8 @@ class ConcurrentSkipList { return; } - NodeType* newHead = NodeType::create(height, value_type(), true); + NodeType* newHead = + NodeType::create(recycler_.alloc(), height, value_type(), true); { // need to guard the head node in case others are adding/removing // nodes linked to the head. @@ -559,7 +500,7 @@ class ConcurrentSkipList { if (!head_.compare_exchange_strong(expected, newHead, std::memory_order_release)) { // if someone has already done the swap, just return. - NodeType::destroy(newHead); + NodeType::destroy(recycler_.alloc(), newHead); return; } oldHead->setMarkedForRemoval(); @@ -571,15 +512,15 @@ class ConcurrentSkipList { recycler_.add(node); } + detail::NodeRecycler recycler_; std::atomic head_; - Recycler recycler_; std::atomic size_; }; -template -class ConcurrentSkipList::Accessor { +template +class ConcurrentSkipList::Accessor { typedef detail::SkipListNode NodeType; - typedef ConcurrentSkipList SkipListType; + typedef ConcurrentSkipList SkipListType; public: typedef T value_type; typedef T key_type; @@ -595,7 +536,7 @@ class ConcurrentSkipList::Accessor { typedef typename SkipListType::const_iterator const_iterator; typedef typename SkipListType::Skipper Skipper; - explicit Accessor(boost::shared_ptr skip_list) + explicit Accessor(std::shared_ptr skip_list) : slHolder_(std::move(skip_list)) { sl_ = slHolder_.get(); @@ -619,7 +560,7 @@ class ConcurrentSkipList::Accessor { Accessor& operator=(const Accessor &accessor) { if (this != &accessor) { slHolder_ = accessor.slHolder_; - sl_->recycler_.release(); + sl_->recycler_.releaseRef(); sl_ = accessor.sl_; sl_->recycler_.addRef(); } @@ -627,7 +568,7 @@ class ConcurrentSkipList::Accessor { } ~Accessor() { - sl_->recycler_.release(); + sl_->recycler_.releaseRef(); } bool empty() const { return sl_->size() == 0; } @@ -706,7 +647,7 @@ class ConcurrentSkipList::Accessor { private: SkipListType *sl_; - boost::shared_ptr slHolder_; + std::shared_ptr slHolder_; }; // implements forward iterator concept. @@ -738,7 +679,7 @@ class detail::csl_iterator : friend class boost::iterator_core_access; template friend class csl_iterator; - void increment() { node_ = node_->next(); }; + void increment() { node_ = node_->next(); } bool equal(const csl_iterator& other) const { return node_ == other.node_; } value_type& dereference() const { return node_->data(); } @@ -746,10 +687,10 @@ class detail::csl_iterator : }; // Skipper interface -template -class ConcurrentSkipList::Skipper { +template +class ConcurrentSkipList::Skipper { typedef detail::SkipListNode NodeType; - typedef ConcurrentSkipList SkipListType; + typedef ConcurrentSkipList SkipListType; typedef typename SkipListType::Accessor Accessor; public: @@ -758,7 +699,7 @@ class ConcurrentSkipList::Skipper { typedef T* pointer; typedef ptrdiff_t difference_type; - Skipper(const boost::shared_ptr& skipList) : + Skipper(const std::shared_ptr& skipList) : accessor_(skipList) { init(); } @@ -777,7 +718,7 @@ class ConcurrentSkipList::Skipper { } int max_layer = maxLayer(); for (int i = 0; i < max_layer; ++i) { - hints_[i] = i + 1; + hints_[i] = uint8_t(i + 1); } hints_[max_layer] = max_layer; } @@ -806,17 +747,17 @@ class ConcurrentSkipList::Skipper { } const value_type &data() const { - DCHECK(succs_[0] != NULL); + DCHECK(succs_[0] != nullptr); return succs_[0]->data(); } value_type &operator *() const { - DCHECK(succs_[0] != NULL); + DCHECK(succs_[0] != nullptr); return succs_[0]->data(); } value_type *operator->() { - DCHECK(succs_[0] != NULL); + DCHECK(succs_[0] != nullptr); return &succs_[0]->data(); } @@ -841,7 +782,8 @@ class ConcurrentSkipList::Skipper { findInsertionPoint(preds_[lyr], lyr, data, preds_, succs_); if (foundLayer < 0) return false; - DCHECK(succs_[0] != NULL) << "lyr=" << lyr << "; max_layer=" << max_layer; + DCHECK(succs_[0] != nullptr) << "lyr=" << lyr + << "; max_layer=" << max_layer; return !succs_[0]->markedForRemoval(); } @@ -857,5 +799,3 @@ class ConcurrentSkipList::Skipper { }; } // namespace folly - -#endif // FOLLY_CONCURRENT_SKIP_LIST_H_