Commit d9abea1b authored by Yiding Jia's avatar Yiding Jia Committed by Facebook GitHub Bot

Back out "Fix concurrency issues in ConcurrentSkipList."

Summary: This is causing a memory leak

Reviewed By: yfeldblum

Differential Revision: D30082875

fbshipit-source-id: 6b6ba9963cc45d71cb98b88b1db9a304cb7b7bda
parent c9875b79
...@@ -97,7 +97,7 @@ class SkipListNode { ...@@ -97,7 +97,7 @@ class SkipListNode {
inline SkipListNode* skip(int layer) const { inline SkipListNode* skip(int layer) const {
DCHECK_LT(layer, height_); DCHECK_LT(layer, height_);
return skip_[layer].load(std::memory_order_acquire); return skip_[layer].load(std::memory_order_consume);
} }
// next valid node as in the linked list // next valid node as in the linked list
...@@ -155,7 +155,7 @@ class SkipListNode { ...@@ -155,7 +155,7 @@ class SkipListNode {
} }
} }
uint16_t getFlags() const { return flags_.load(std::memory_order_acquire); } uint16_t getFlags() const { return flags_.load(std::memory_order_consume); }
void setFlags(uint16_t flags) { void setFlags(uint16_t flags) {
flags_.store(flags, std::memory_order_release); flags_.store(flags, std::memory_order_release);
} }
...@@ -269,41 +269,43 @@ class NodeRecycler< ...@@ -269,41 +269,43 @@ class NodeRecycler<
dirty_.store(true, std::memory_order_relaxed); dirty_.store(true, std::memory_order_relaxed);
} }
int addRef() { return refs_.fetch_add(1, std::memory_order_acq_rel); } int addRef() { return refs_.fetch_add(1, std::memory_order_relaxed); }
int releaseRef() { int releaseRef() {
// This if statement is purely an optimization. It's possible that this // We don't expect to clean the recycler immediately everytime it is OK
// misses an opportunity to delete, but that's OK, we'll try again at // to do so. Here, it is possible that multiple accessors all release at
// the next opportunity. It does not harm the thread safety. For this // the same time but nobody would clean the recycler here. If this
// reason, we can use relaxed loads to make the decision. // happens, the recycler will usually still get cleaned when
if (!dirty_.load(std::memory_order_relaxed) || refs() > 1) { // such a race doesn't happen. The worst case is the recycler will
return refs_.fetch_add(-1, std::memory_order_acq_rel); // 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);
} }
std::unique_ptr<std::vector<NodeType*>> newNodes; std::unique_ptr<std::vector<NodeType*>> newNodes;
int ret;
{ {
// The order at which we lock, add, swap, is very important for
// correctness.
std::lock_guard<MicroSpinLock> g(lock_); std::lock_guard<MicroSpinLock> g(lock_);
ret = refs_.fetch_add(-1, std::memory_order_acq_rel); if (nodes_.get() == nullptr || refs() > 1) {
if (ret == 0) { return refs_.fetch_add(-1, std::memory_order_relaxed);
// When refs_ reachs 0, 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 this.
newNodes.swap(nodes_);
dirty_.store(false, 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 // TODO(xliu) should we spawn a thread to do this when there are large
// number of nodes in the recycler? // number of nodes in the recycler?
if (newNodes) { for (auto& node : *newNodes) {
for (auto& node : *newNodes) { NodeType::destroy(alloc_, node);
NodeType::destroy(alloc_, node);
}
} }
return ret;
// 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);
} }
NodeAlloc& alloc() { return alloc_; } NodeAlloc& alloc() { return alloc_; }
......
...@@ -250,7 +250,7 @@ class ConcurrentSkipList { ...@@ -250,7 +250,7 @@ class ConcurrentSkipList {
return foundLayer; return foundLayer;
} }
int height() const { return head_.load(std::memory_order_acquire)->height(); } int height() const { return head_.load(std::memory_order_consume)->height(); }
int maxLayer() const { return height() - 1; } int maxLayer() const { return height() - 1; }
...@@ -401,12 +401,12 @@ class ConcurrentSkipList { ...@@ -401,12 +401,12 @@ class ConcurrentSkipList {
} }
const value_type* first() const { const value_type* first() const {
auto node = head_.load(std::memory_order_acquire)->skip(0); auto node = head_.load(std::memory_order_consume)->skip(0);
return node ? &node->data() : nullptr; return node ? &node->data() : nullptr;
} }
const value_type* last() const { const value_type* last() const {
NodeType* pred = head_.load(std::memory_order_acquire); NodeType* pred = head_.load(std::memory_order_consume);
NodeType* node = nullptr; NodeType* node = nullptr;
for (int layer = maxLayer(); layer >= 0; --layer) { for (int layer = maxLayer(); layer >= 0; --layer) {
do { do {
...@@ -434,7 +434,7 @@ class ConcurrentSkipList { ...@@ -434,7 +434,7 @@ class ConcurrentSkipList {
int* max_layer) const { int* max_layer) const {
*max_layer = maxLayer(); *max_layer = maxLayer();
return findInsertionPoint( return findInsertionPoint(
head_.load(std::memory_order_acquire), *max_layer, data, preds, succs); head_.load(std::memory_order_consume), *max_layer, data, preds, succs);
} }
// Find node for access. Returns a paired values: // Find node for access. Returns a paired values:
...@@ -450,7 +450,7 @@ class ConcurrentSkipList { ...@@ -450,7 +450,7 @@ class ConcurrentSkipList {
// results, this is slightly faster than findNodeRightDown for better // results, this is slightly faster than findNodeRightDown for better
// localality on the skipping pointers. // localality on the skipping pointers.
std::pair<NodeType*, int> findNodeDownRight(const value_type& data) const { std::pair<NodeType*, int> findNodeDownRight(const value_type& data) const {
NodeType* pred = head_.load(std::memory_order_acquire); NodeType* pred = head_.load(std::memory_order_consume);
int ht = pred->height(); int ht = pred->height();
NodeType* node = nullptr; NodeType* node = nullptr;
...@@ -478,7 +478,7 @@ class ConcurrentSkipList { ...@@ -478,7 +478,7 @@ class ConcurrentSkipList {
// find node by first stepping right then stepping down. // find node by first stepping right then stepping down.
// We still keep this for reference purposes. // We still keep this for reference purposes.
std::pair<NodeType*, int> findNodeRightDown(const value_type& data) const { std::pair<NodeType*, int> findNodeRightDown(const value_type& data) const {
NodeType* pred = head_.load(std::memory_order_acquire); NodeType* pred = head_.load(std::memory_order_consume);
NodeType* node = nullptr; NodeType* node = nullptr;
auto top = maxLayer(); auto top = maxLayer();
int found = 0; int found = 0;
...@@ -502,7 +502,7 @@ class ConcurrentSkipList { ...@@ -502,7 +502,7 @@ class ConcurrentSkipList {
} }
void growHeight(int height) { void growHeight(int height) {
NodeType* oldHead = head_.load(std::memory_order_acquire); NodeType* oldHead = head_.load(std::memory_order_consume);
if (oldHead->height() >= height) { // someone else already did this if (oldHead->height() >= height) { // someone else already did this
return; return;
} }
...@@ -598,7 +598,7 @@ class ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>::Accessor { ...@@ -598,7 +598,7 @@ class ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>::Accessor {
size_type count(const key_type& data) const { return contains(data); } size_type count(const key_type& data) const { return contains(data); }
iterator begin() const { iterator begin() const {
NodeType* head = sl_->head_.load(std::memory_order_acquire); NodeType* head = sl_->head_.load(std::memory_order_consume);
return iterator(head->next()); return iterator(head->next());
} }
iterator end() const { return iterator(nullptr); } iterator end() const { return iterator(nullptr); }
...@@ -814,7 +814,7 @@ class ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>::Skipper { ...@@ -814,7 +814,7 @@ class ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>::Skipper {
private: private:
NodeType* head() const { NodeType* head() const {
return accessor_.skiplist()->head_.load(std::memory_order_acquire); return accessor_.skiplist()->head_.load(std::memory_order_consume);
} }
Accessor accessor_; Accessor accessor_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment