Fix utils::SkipList atomic memory order
Reviewers: mtomic Reviewed By: mtomic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2120
This commit is contained in:
parent
775930ef4e
commit
70c14b2048
@ -17,6 +17,12 @@
|
|||||||
#include "utils/spin_lock.hpp"
|
#include "utils/spin_lock.hpp"
|
||||||
#include "utils/stack.hpp"
|
#include "utils/stack.hpp"
|
||||||
|
|
||||||
|
// This code heavily depends on atomic operations. For a more detailed
|
||||||
|
// description of how exactly atomic operations work, see:
|
||||||
|
// https://www.codeproject.com/Articles/1183423/We-make-a-std-shared-mutex-times-faster
|
||||||
|
// How the specified memory fences influence the generated assembler
|
||||||
|
// instructions, see: https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
|
||||||
|
|
||||||
namespace utils {
|
namespace utils {
|
||||||
|
|
||||||
/// This is the maximum height of the list. This value shouldn't be changed from
|
/// This is the maximum height of the list. This value shouldn't be changed from
|
||||||
@ -129,19 +135,19 @@ class SkipListGc final {
|
|||||||
|
|
||||||
Block *AllocateBlock(Block *head) {
|
Block *AllocateBlock(Block *head) {
|
||||||
std::lock_guard<SpinLock> guard(lock_);
|
std::lock_guard<SpinLock> guard(lock_);
|
||||||
Block *curr_head = head_.load(std::memory_order_relaxed);
|
Block *curr_head = head_.load(std::memory_order_acquire);
|
||||||
if (curr_head == head) {
|
if (curr_head == head) {
|
||||||
Block *block = new (calloc(1, sizeof(Block))) Block();
|
Block *block = new (calloc(1, sizeof(Block))) Block();
|
||||||
block->prev.store(curr_head, std::memory_order_relaxed);
|
block->prev.store(curr_head, std::memory_order_release);
|
||||||
block->succ.store(nullptr, std::memory_order_relaxed);
|
block->succ.store(nullptr, std::memory_order_release);
|
||||||
block->first_id = last_id_;
|
block->first_id = last_id_;
|
||||||
last_id_ += kIdsInBlock;
|
last_id_ += kIdsInBlock;
|
||||||
if (curr_head == nullptr) {
|
if (curr_head == nullptr) {
|
||||||
tail_.store(block, std::memory_order_relaxed);
|
tail_.store(block, std::memory_order_release);
|
||||||
} else {
|
} else {
|
||||||
curr_head->succ.store(block, std::memory_order_relaxed);
|
curr_head->succ.store(block, std::memory_order_release);
|
||||||
}
|
}
|
||||||
head_.store(block, std::memory_order_relaxed);
|
head_.store(block, std::memory_order_release);
|
||||||
return block;
|
return block;
|
||||||
} else {
|
} else {
|
||||||
return curr_head;
|
return curr_head;
|
||||||
@ -162,9 +168,9 @@ class SkipListGc final {
|
|||||||
SkipListGc &operator=(SkipListGc &&other) = delete;
|
SkipListGc &operator=(SkipListGc &&other) = delete;
|
||||||
|
|
||||||
~SkipListGc() {
|
~SkipListGc() {
|
||||||
Block *head = head_.load(std::memory_order_relaxed);
|
Block *head = head_.load(std::memory_order_acquire);
|
||||||
while (head != nullptr) {
|
while (head != nullptr) {
|
||||||
Block *prev = head->prev.load(std::memory_order_relaxed);
|
Block *prev = head->prev.load(std::memory_order_acquire);
|
||||||
head->~Block();
|
head->~Block();
|
||||||
free(head);
|
free(head);
|
||||||
head = prev;
|
head = prev;
|
||||||
@ -177,7 +183,7 @@ class SkipListGc final {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint64_t AllocateId() {
|
uint64_t AllocateId() {
|
||||||
return accessor_id_.fetch_add(1, std::memory_order_relaxed);
|
return accessor_id_.fetch_add(1, std::memory_order_acq_rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReleaseId(uint64_t id) {
|
void ReleaseId(uint64_t id) {
|
||||||
@ -188,14 +194,14 @@ class SkipListGc final {
|
|||||||
// accessed without a lock because all of the pointers in the list are
|
// accessed without a lock because all of the pointers in the list are
|
||||||
// atomic and their modification is done so that the access is always
|
// atomic and their modification is done so that the access is always
|
||||||
// correct.
|
// correct.
|
||||||
Block *head = head_.load(std::memory_order_relaxed);
|
Block *head = head_.load(std::memory_order_acquire);
|
||||||
if (head == nullptr) {
|
if (head == nullptr) {
|
||||||
head = AllocateBlock(head);
|
head = AllocateBlock(head);
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
CHECK(head != nullptr) << "Missing SkipListGc block!";
|
CHECK(head != nullptr) << "Missing SkipListGc block!";
|
||||||
if (id < head->first_id) {
|
if (id < head->first_id) {
|
||||||
head = head->prev.load(std::memory_order_relaxed);
|
head = head->prev.load(std::memory_order_acquire);
|
||||||
} else if (id >= head->first_id + kIdsInBlock) {
|
} else if (id >= head->first_id + kIdsInBlock) {
|
||||||
head = AllocateBlock(head);
|
head = AllocateBlock(head);
|
||||||
} else {
|
} else {
|
||||||
@ -205,7 +211,7 @@ class SkipListGc final {
|
|||||||
uint64_t value = 1;
|
uint64_t value = 1;
|
||||||
value <<= bit;
|
value <<= bit;
|
||||||
auto ret =
|
auto ret =
|
||||||
head->field[field].fetch_or(value, std::memory_order_relaxed);
|
head->field[field].fetch_or(value, std::memory_order_acq_rel);
|
||||||
CHECK(!(ret & value)) << "A SkipList Accessor was released twice!";
|
CHECK(!(ret & value)) << "A SkipList Accessor was released twice!";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -214,18 +220,18 @@ class SkipListGc final {
|
|||||||
|
|
||||||
void Collect(TNode *node) {
|
void Collect(TNode *node) {
|
||||||
std::lock_guard<SpinLock> guard(lock_);
|
std::lock_guard<SpinLock> guard(lock_);
|
||||||
deleted_.Push({accessor_id_.load(std::memory_order_relaxed), node});
|
deleted_.Push({accessor_id_.load(std::memory_order_acquire), node});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Run() {
|
void Run() {
|
||||||
if (!lock_.try_lock()) return;
|
if (!lock_.try_lock()) return;
|
||||||
OnScopeExit cleanup([&] { lock_.unlock(); });
|
OnScopeExit cleanup([&] { lock_.unlock(); });
|
||||||
Block *tail = tail_.load(std::memory_order_relaxed);
|
Block *tail = tail_.load(std::memory_order_acquire);
|
||||||
uint64_t last_dead = 0;
|
uint64_t last_dead = 0;
|
||||||
bool remove_block = true;
|
bool remove_block = true;
|
||||||
while (tail != nullptr && remove_block) {
|
while (tail != nullptr && remove_block) {
|
||||||
for (uint64_t pos = 0; pos < kSkipListGcBlockSize; ++pos) {
|
for (uint64_t pos = 0; pos < kSkipListGcBlockSize; ++pos) {
|
||||||
uint64_t field = tail->field[pos].load(std::memory_order_relaxed);
|
uint64_t field = tail->field[pos].load(std::memory_order_acquire);
|
||||||
if (field != std::numeric_limits<uint64_t>::max()) {
|
if (field != std::numeric_limits<uint64_t>::max()) {
|
||||||
if (field != 0) {
|
if (field != 0) {
|
||||||
// Here we find the position of the least significant zero bit
|
// Here we find the position of the least significant zero bit
|
||||||
@ -246,7 +252,7 @@ class SkipListGc final {
|
|||||||
last_dead = tail->first_id + (pos + 1) * kIdsInField - 1;
|
last_dead = tail->first_id + (pos + 1) * kIdsInField - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Block *next = tail->succ.load(std::memory_order_relaxed);
|
Block *next = tail->succ.load(std::memory_order_acquire);
|
||||||
// Here we also check whether the next block isn't a `nullptr`. If it is
|
// Here we also check whether the next block isn't a `nullptr`. If it is
|
||||||
// `nullptr` that means that this is the last existing block. We don't
|
// `nullptr` that means that this is the last existing block. We don't
|
||||||
// want to destroy it because we would need to change the `head_` to point
|
// want to destroy it because we would need to change the `head_` to point
|
||||||
@ -254,10 +260,10 @@ class SkipListGc final {
|
|||||||
// thread doesn't have a pointer to the block that it got from reading
|
// thread doesn't have a pointer to the block that it got from reading
|
||||||
// `head_`. We bail out here, this block will be freed next time.
|
// `head_`. We bail out here, this block will be freed next time.
|
||||||
if (remove_block && next != nullptr) {
|
if (remove_block && next != nullptr) {
|
||||||
CHECK(tail == tail_.load(std::memory_order_relaxed))
|
CHECK(tail == tail_.load(std::memory_order_acquire))
|
||||||
<< "Can't remove SkipListGc block that is in the middle!";
|
<< "Can't remove SkipListGc block that is in the middle!";
|
||||||
next->prev.store(nullptr, std::memory_order_relaxed);
|
next->prev.store(nullptr, std::memory_order_release);
|
||||||
tail_.store(next, std::memory_order_relaxed);
|
tail_.store(next, std::memory_order_release);
|
||||||
// Destroy the block.
|
// Destroy the block.
|
||||||
tail->~Block();
|
tail->~Block();
|
||||||
free(tail);
|
free(tail);
|
||||||
@ -479,8 +485,8 @@ class SkipList final {
|
|||||||
|
|
||||||
Iterator &operator++() {
|
Iterator &operator++() {
|
||||||
while (true) {
|
while (true) {
|
||||||
node_ = node_->nexts[0].load(std::memory_order_relaxed);
|
node_ = node_->nexts[0].load(std::memory_order_acquire);
|
||||||
if (node_ != nullptr && node_->marked.load(std::memory_order_relaxed)) {
|
if (node_ != nullptr && node_->marked.load(std::memory_order_acquire)) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
return *this;
|
return *this;
|
||||||
@ -515,8 +521,8 @@ class SkipList final {
|
|||||||
|
|
||||||
ConstIterator &operator++() {
|
ConstIterator &operator++() {
|
||||||
while (true) {
|
while (true) {
|
||||||
TNode *next = node_->nexts[0].load(std::memory_order_relaxed);
|
TNode *next = node_->nexts[0].load(std::memory_order_acquire);
|
||||||
if (next == nullptr || !next->marked.load(std::memory_order_relaxed)) {
|
if (next == nullptr || !next->marked.load(std::memory_order_acquire)) {
|
||||||
node_ = next;
|
node_ = next;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -559,15 +565,15 @@ class SkipList final {
|
|||||||
/// the list.
|
/// the list.
|
||||||
Iterator begin() {
|
Iterator begin() {
|
||||||
return Iterator{
|
return Iterator{
|
||||||
skiplist_->head_->nexts[0].load(std::memory_order_relaxed)};
|
skiplist_->head_->nexts[0].load(std::memory_order_acquire)};
|
||||||
}
|
}
|
||||||
ConstIterator begin() const {
|
ConstIterator begin() const {
|
||||||
return ConstIterator{
|
return ConstIterator{
|
||||||
skiplist_->head_->nexts[0].load(std::memory_order_relaxed)};
|
skiplist_->head_->nexts[0].load(std::memory_order_acquire)};
|
||||||
}
|
}
|
||||||
ConstIterator cbegin() const {
|
ConstIterator cbegin() const {
|
||||||
return ConstIterator{
|
return ConstIterator{
|
||||||
skiplist_->head_->nexts[0].load(std::memory_order_relaxed)};
|
skiplist_->head_->nexts[0].load(std::memory_order_acquire)};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Functions that return an Iterator (or ConstIterator) to the end of the
|
/// Functions that return an Iterator (or ConstIterator) to the end of the
|
||||||
@ -698,11 +704,11 @@ class SkipList final {
|
|||||||
|
|
||||||
ConstIterator begin() const {
|
ConstIterator begin() const {
|
||||||
return ConstIterator{
|
return ConstIterator{
|
||||||
skiplist_->head_->nexts[0].load(std::memory_order_relaxed)};
|
skiplist_->head_->nexts[0].load(std::memory_order_acquire)};
|
||||||
}
|
}
|
||||||
ConstIterator cbegin() const {
|
ConstIterator cbegin() const {
|
||||||
return ConstIterator{
|
return ConstIterator{
|
||||||
skiplist_->head_->nexts[0].load(std::memory_order_relaxed)};
|
skiplist_->head_->nexts[0].load(std::memory_order_acquire)};
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstIterator end() const { return ConstIterator{nullptr}; }
|
ConstIterator end() const { return ConstIterator{nullptr}; }
|
||||||
@ -766,7 +772,7 @@ class SkipList final {
|
|||||||
SkipList &operator=(SkipList &&other) noexcept {
|
SkipList &operator=(SkipList &&other) noexcept {
|
||||||
TNode *head = head_;
|
TNode *head = head_;
|
||||||
while (head != nullptr) {
|
while (head != nullptr) {
|
||||||
TNode *succ = head->nexts[0].load(std::memory_order_relaxed);
|
TNode *succ = head->nexts[0].load(std::memory_order_acquire);
|
||||||
head->~TNode();
|
head->~TNode();
|
||||||
free(head);
|
free(head);
|
||||||
head = succ;
|
head = succ;
|
||||||
@ -783,7 +789,7 @@ class SkipList final {
|
|||||||
~SkipList() {
|
~SkipList() {
|
||||||
TNode *head = head_;
|
TNode *head = head_;
|
||||||
while (head != nullptr) {
|
while (head != nullptr) {
|
||||||
TNode *succ = head->nexts[0].load(std::memory_order_relaxed);
|
TNode *succ = head->nexts[0].load(std::memory_order_acquire);
|
||||||
head->~TNode();
|
head->~TNode();
|
||||||
free(head);
|
free(head);
|
||||||
head = succ;
|
head = succ;
|
||||||
@ -797,7 +803,7 @@ class SkipList final {
|
|||||||
|
|
||||||
/// The size of the list can be read directly from the list because it is an
|
/// The size of the list can be read directly from the list because it is an
|
||||||
/// atomic operation.
|
/// atomic operation.
|
||||||
uint64_t size() const { return size_.load(std::memory_order_relaxed); }
|
uint64_t size() const { return size_.load(std::memory_order_acquire); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename TKey>
|
template <typename TKey>
|
||||||
@ -805,11 +811,11 @@ class SkipList final {
|
|||||||
int layer_found = -1;
|
int layer_found = -1;
|
||||||
TNode *pred = head_;
|
TNode *pred = head_;
|
||||||
for (int layer = kSkipListMaxHeight - 1; layer >= 0; --layer) {
|
for (int layer = kSkipListMaxHeight - 1; layer >= 0; --layer) {
|
||||||
TNode *curr = pred->nexts[layer].load(std::memory_order_relaxed);
|
TNode *curr = pred->nexts[layer].load(std::memory_order_acquire);
|
||||||
// Existence test is missing in the paper.
|
// Existence test is missing in the paper.
|
||||||
while (curr != nullptr && curr->obj < key) {
|
while (curr != nullptr && curr->obj < key) {
|
||||||
pred = curr;
|
pred = curr;
|
||||||
curr = pred->nexts[layer].load(std::memory_order_relaxed);
|
curr = pred->nexts[layer].load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
// Existence test is missing in the paper.
|
// Existence test is missing in the paper.
|
||||||
if (layer_found == -1 && curr && curr->obj == key) {
|
if (layer_found == -1 && curr && curr->obj == key) {
|
||||||
@ -831,8 +837,8 @@ class SkipList final {
|
|||||||
int layer_found = find_node(object, preds, succs);
|
int layer_found = find_node(object, preds, succs);
|
||||||
if (layer_found != -1) {
|
if (layer_found != -1) {
|
||||||
TNode *node_found = succs[layer_found];
|
TNode *node_found = succs[layer_found];
|
||||||
if (!node_found->marked.load(std::memory_order_relaxed)) {
|
if (!node_found->marked.load(std::memory_order_acquire)) {
|
||||||
while (!node_found->fully_linked.load(std::memory_order_relaxed))
|
while (!node_found->fully_linked.load(std::memory_order_acquire))
|
||||||
;
|
;
|
||||||
return {Iterator{node_found}, false};
|
return {Iterator{node_found}, false};
|
||||||
}
|
}
|
||||||
@ -853,9 +859,9 @@ class SkipList final {
|
|||||||
}
|
}
|
||||||
// Existence test is missing in the paper.
|
// Existence test is missing in the paper.
|
||||||
valid =
|
valid =
|
||||||
!pred->marked.load(std::memory_order_relaxed) &&
|
!pred->marked.load(std::memory_order_acquire) &&
|
||||||
pred->nexts[layer].load(std::memory_order_relaxed) == succ &&
|
pred->nexts[layer].load(std::memory_order_acquire) == succ &&
|
||||||
(succ == nullptr || !succ->marked.load(std::memory_order_relaxed));
|
(succ == nullptr || !succ->marked.load(std::memory_order_acquire));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) continue;
|
if (!valid) continue;
|
||||||
@ -871,12 +877,12 @@ class SkipList final {
|
|||||||
// The paper is also wrong here. It states that the loop should go up to
|
// The paper is also wrong here. It states that the loop should go up to
|
||||||
// `top_layer` which is wrong.
|
// `top_layer` which is wrong.
|
||||||
for (int layer = 0; layer < top_layer; ++layer) {
|
for (int layer = 0; layer < top_layer; ++layer) {
|
||||||
new_node->nexts[layer].store(succs[layer], std::memory_order_relaxed);
|
new_node->nexts[layer].store(succs[layer], std::memory_order_release);
|
||||||
preds[layer]->nexts[layer].store(new_node, std::memory_order_relaxed);
|
preds[layer]->nexts[layer].store(new_node, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_node->fully_linked.store(true, std::memory_order_relaxed);
|
new_node->fully_linked.store(true, std::memory_order_release);
|
||||||
size_.fetch_add(1, std::memory_order_relaxed);
|
size_.fetch_add(1, std::memory_order_acq_rel);
|
||||||
return {Iterator{new_node}, true};
|
return {Iterator{new_node}, true};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -886,8 +892,8 @@ class SkipList final {
|
|||||||
TNode *preds[kSkipListMaxHeight], *succs[kSkipListMaxHeight];
|
TNode *preds[kSkipListMaxHeight], *succs[kSkipListMaxHeight];
|
||||||
int layer_found = find_node(key, preds, succs);
|
int layer_found = find_node(key, preds, succs);
|
||||||
return (layer_found != -1 &&
|
return (layer_found != -1 &&
|
||||||
succs[layer_found]->fully_linked.load(std::memory_order_relaxed) &&
|
succs[layer_found]->fully_linked.load(std::memory_order_acquire) &&
|
||||||
!succs[layer_found]->marked.load(std::memory_order_relaxed));
|
!succs[layer_found]->marked.load(std::memory_order_acquire));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey>
|
template <typename TKey>
|
||||||
@ -895,8 +901,8 @@ class SkipList final {
|
|||||||
TNode *preds[kSkipListMaxHeight], *succs[kSkipListMaxHeight];
|
TNode *preds[kSkipListMaxHeight], *succs[kSkipListMaxHeight];
|
||||||
int layer_found = find_node(key, preds, succs);
|
int layer_found = find_node(key, preds, succs);
|
||||||
if (layer_found != -1 &&
|
if (layer_found != -1 &&
|
||||||
succs[layer_found]->fully_linked.load(std::memory_order_relaxed) &&
|
succs[layer_found]->fully_linked.load(std::memory_order_acquire) &&
|
||||||
!succs[layer_found]->marked.load(std::memory_order_relaxed)) {
|
!succs[layer_found]->marked.load(std::memory_order_acquire)) {
|
||||||
return Iterator{succs[layer_found]};
|
return Iterator{succs[layer_found]};
|
||||||
}
|
}
|
||||||
return Iterator{nullptr};
|
return Iterator{nullptr};
|
||||||
@ -906,8 +912,8 @@ class SkipList final {
|
|||||||
Iterator find_equal_or_greater(const TKey &key) const {
|
Iterator find_equal_or_greater(const TKey &key) const {
|
||||||
TNode *preds[kSkipListMaxHeight], *succs[kSkipListMaxHeight];
|
TNode *preds[kSkipListMaxHeight], *succs[kSkipListMaxHeight];
|
||||||
find_node(key, preds, succs);
|
find_node(key, preds, succs);
|
||||||
if (succs[0] && succs[0]->fully_linked.load(std::memory_order_relaxed) &&
|
if (succs[0] && succs[0]->fully_linked.load(std::memory_order_acquire) &&
|
||||||
!succs[0]->marked.load(std::memory_order_relaxed)) {
|
!succs[0]->marked.load(std::memory_order_acquire)) {
|
||||||
return Iterator{succs[0]};
|
return Iterator{succs[0]};
|
||||||
}
|
}
|
||||||
return Iterator{nullptr};
|
return Iterator{nullptr};
|
||||||
@ -930,14 +936,14 @@ class SkipList final {
|
|||||||
for (int layer = std::min(layer_found, max_layer_for_estimation - 1);
|
for (int layer = std::min(layer_found, max_layer_for_estimation - 1);
|
||||||
layer >= 0; --layer) {
|
layer >= 0; --layer) {
|
||||||
uint64_t nodes_traversed = 0;
|
uint64_t nodes_traversed = 0;
|
||||||
TNode *curr = pred->nexts[layer].load(std::memory_order_relaxed);
|
TNode *curr = pred->nexts[layer].load(std::memory_order_acquire);
|
||||||
while (curr != nullptr && curr->obj < key) {
|
while (curr != nullptr && curr->obj < key) {
|
||||||
pred = curr;
|
pred = curr;
|
||||||
curr = pred->nexts[layer].load(std::memory_order_relaxed);
|
curr = pred->nexts[layer].load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
while (curr != nullptr && curr->obj == key) {
|
while (curr != nullptr && curr->obj == key) {
|
||||||
pred = curr;
|
pred = curr;
|
||||||
curr = pred->nexts[layer].load(std::memory_order_relaxed);
|
curr = pred->nexts[layer].load(std::memory_order_acquire);
|
||||||
++nodes_traversed;
|
++nodes_traversed;
|
||||||
}
|
}
|
||||||
// Here we assume that the list is perfectly balanced and that each upper
|
// Here we assume that the list is perfectly balanced and that each upper
|
||||||
@ -975,36 +981,36 @@ class SkipList final {
|
|||||||
for (int layer = std::min(layer_found, max_layer_for_estimation - 1);
|
for (int layer = std::min(layer_found, max_layer_for_estimation - 1);
|
||||||
layer >= 0; --layer) {
|
layer >= 0; --layer) {
|
||||||
uint64_t nodes_traversed = 0;
|
uint64_t nodes_traversed = 0;
|
||||||
TNode *curr = pred->nexts[layer].load(std::memory_order_relaxed);
|
TNode *curr = pred->nexts[layer].load(std::memory_order_acquire);
|
||||||
if (lower) {
|
if (lower) {
|
||||||
while (curr != nullptr && curr->obj < lower->value()) {
|
while (curr != nullptr && curr->obj < lower->value()) {
|
||||||
pred = curr;
|
pred = curr;
|
||||||
curr = pred->nexts[layer].load(std::memory_order_relaxed);
|
curr = pred->nexts[layer].load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
if (lower->IsExclusive()) {
|
if (lower->IsExclusive()) {
|
||||||
while (curr != nullptr && curr->obj == lower->value()) {
|
while (curr != nullptr && curr->obj == lower->value()) {
|
||||||
pred = curr;
|
pred = curr;
|
||||||
curr = pred->nexts[layer].load(std::memory_order_relaxed);
|
curr = pred->nexts[layer].load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (upper) {
|
if (upper) {
|
||||||
while (curr != nullptr && curr->obj < upper->value()) {
|
while (curr != nullptr && curr->obj < upper->value()) {
|
||||||
pred = curr;
|
pred = curr;
|
||||||
curr = pred->nexts[layer].load(std::memory_order_relaxed);
|
curr = pred->nexts[layer].load(std::memory_order_acquire);
|
||||||
++nodes_traversed;
|
++nodes_traversed;
|
||||||
}
|
}
|
||||||
if (upper->IsInclusive()) {
|
if (upper->IsInclusive()) {
|
||||||
while (curr != nullptr && curr->obj == upper->value()) {
|
while (curr != nullptr && curr->obj == upper->value()) {
|
||||||
pred = curr;
|
pred = curr;
|
||||||
curr = pred->nexts[layer].load(std::memory_order_relaxed);
|
curr = pred->nexts[layer].load(std::memory_order_acquire);
|
||||||
++nodes_traversed;
|
++nodes_traversed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (curr != nullptr) {
|
while (curr != nullptr) {
|
||||||
pred = curr;
|
pred = curr;
|
||||||
curr = pred->nexts[layer].load(std::memory_order_relaxed);
|
curr = pred->nexts[layer].load(std::memory_order_acquire);
|
||||||
++nodes_traversed;
|
++nodes_traversed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1019,9 +1025,9 @@ class SkipList final {
|
|||||||
bool ok_to_delete(TNode *candidate, int layer_found) {
|
bool ok_to_delete(TNode *candidate, int layer_found) {
|
||||||
// The paper has an incorrect check here. It expects the `layer_found`
|
// The paper has an incorrect check here. It expects the `layer_found`
|
||||||
// variable to be 1-indexed, but in fact it is 0-indexed.
|
// variable to be 1-indexed, but in fact it is 0-indexed.
|
||||||
return (candidate->fully_linked.load(std::memory_order_relaxed) &&
|
return (candidate->fully_linked.load(std::memory_order_acquire) &&
|
||||||
candidate->height == layer_found + 1 &&
|
candidate->height == layer_found + 1 &&
|
||||||
!candidate->marked.load(std::memory_order_relaxed));
|
!candidate->marked.load(std::memory_order_acquire));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey>
|
template <typename TKey>
|
||||||
@ -1039,10 +1045,10 @@ class SkipList final {
|
|||||||
node_to_delete = succs[layer_found];
|
node_to_delete = succs[layer_found];
|
||||||
top_layer = node_to_delete->height;
|
top_layer = node_to_delete->height;
|
||||||
node_guard = std::unique_lock<SpinLock>(node_to_delete->lock);
|
node_guard = std::unique_lock<SpinLock>(node_to_delete->lock);
|
||||||
if (node_to_delete->marked.load(std::memory_order_relaxed)) {
|
if (node_to_delete->marked.load(std::memory_order_acquire)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
node_to_delete->marked.store(true, std::memory_order_relaxed);
|
node_to_delete->marked.store(true, std::memory_order_release);
|
||||||
is_marked = true;
|
is_marked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1058,8 +1064,8 @@ class SkipList final {
|
|||||||
guards[layer] = std::unique_lock<SpinLock>(pred->lock);
|
guards[layer] = std::unique_lock<SpinLock>(pred->lock);
|
||||||
prev_pred = pred;
|
prev_pred = pred;
|
||||||
}
|
}
|
||||||
valid = !pred->marked.load(std::memory_order_relaxed) &&
|
valid = !pred->marked.load(std::memory_order_acquire) &&
|
||||||
pred->nexts[layer].load(std::memory_order_relaxed) == succ;
|
pred->nexts[layer].load(std::memory_order_acquire) == succ;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) continue;
|
if (!valid) continue;
|
||||||
@ -1068,11 +1074,11 @@ class SkipList final {
|
|||||||
// from `top_layer` which is wrong.
|
// from `top_layer` which is wrong.
|
||||||
for (int layer = top_layer - 1; layer >= 0; --layer) {
|
for (int layer = top_layer - 1; layer >= 0; --layer) {
|
||||||
preds[layer]->nexts[layer].store(
|
preds[layer]->nexts[layer].store(
|
||||||
node_to_delete->nexts[layer].load(std::memory_order_relaxed),
|
node_to_delete->nexts[layer].load(std::memory_order_acquire),
|
||||||
std::memory_order_relaxed);
|
std::memory_order_release);
|
||||||
}
|
}
|
||||||
gc_.Collect(node_to_delete);
|
gc_.Collect(node_to_delete);
|
||||||
size_.fetch_add(-1, std::memory_order_relaxed);
|
size_.fetch_add(-1, std::memory_order_acq_rel);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
Loading…
Reference in New Issue
Block a user