mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
add: [concur] intrusive stack implementation
This commit is contained in:
parent
9bd8813d46
commit
bdc8839c0c
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* \file libconcur/queue.h
|
||||
* \author mutouyun (orz@orzz.org)
|
||||
* \brief Define concurrent queue.
|
||||
* \brief Define concurrent circular queue.
|
||||
* \date 2022-11-19
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
59
include/libconcur/intrusive_stack.h
Normal file
59
include/libconcur/intrusive_stack.h
Normal file
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* \file libconcur/intrusive_stack.h
|
||||
* \author mutouyun (orz@orzz.org)
|
||||
* \brief Define concurrent intrusive stack.
|
||||
* \date 2023-11-18
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "libconcur/def.h"
|
||||
|
||||
LIBCONCUR_NAMESPACE_BEG_
|
||||
|
||||
template <typename T>
|
||||
class intrusive_stack {
|
||||
public:
|
||||
struct node {
|
||||
T value;
|
||||
std::atomic<node *> next{nullptr};
|
||||
};
|
||||
|
||||
private:
|
||||
std::atomic<node *> top_{nullptr};
|
||||
|
||||
public:
|
||||
intrusive_stack(intrusive_stack const &) = delete;
|
||||
intrusive_stack(intrusive_stack &&) = delete;
|
||||
intrusive_stack &operator=(intrusive_stack const &) = delete;
|
||||
intrusive_stack &operator=(intrusive_stack &&) = delete;
|
||||
|
||||
constexpr intrusive_stack() noexcept = default;
|
||||
|
||||
bool empty() const noexcept {
|
||||
return top_.load(std::memory_order_acquire) == nullptr;
|
||||
}
|
||||
|
||||
void push(node *n) noexcept {
|
||||
node *old_top = top_.load(std::memory_order_acquire);
|
||||
do {
|
||||
n->next.store(old_top, std::memory_order_relaxed);
|
||||
} while (!top_.compare_exchange_weak(old_top, n, std::memory_order_release
|
||||
, std::memory_order_acquire));
|
||||
}
|
||||
|
||||
node *pop() noexcept {
|
||||
node *old_top = top_.load(std::memory_order_acquire);
|
||||
do {
|
||||
if (old_top == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
} while (!top_.compare_exchange_weak(old_top, old_top->next.load(std::memory_order_relaxed)
|
||||
, std::memory_order_release
|
||||
, std::memory_order_acquire));
|
||||
return old_top;
|
||||
}
|
||||
};
|
||||
|
||||
LIBCONCUR_NAMESPACE_END_
|
||||
92
test/concur/test_concur_stack.cpp
Normal file
92
test/concur/test_concur_stack.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#define private public
|
||||
#include "libconcur/intrusive_stack.h"
|
||||
|
||||
TEST(intrusive_stack, construct) {
|
||||
concur::intrusive_stack<int> s;
|
||||
EXPECT_TRUE(s.empty());
|
||||
}
|
||||
|
||||
TEST(intrusive_stack, construct_node) {
|
||||
concur::intrusive_stack<int>::node n;
|
||||
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == nullptr);
|
||||
}
|
||||
|
||||
TEST(intrusive_stack, copyable) {
|
||||
EXPECT_FALSE(std::is_copy_constructible<concur::intrusive_stack<int>>::value);
|
||||
EXPECT_FALSE(std::is_copy_assignable<concur::intrusive_stack<int>>::value);
|
||||
}
|
||||
|
||||
TEST(intrusive_stack, moveable) {
|
||||
EXPECT_FALSE(std::is_move_constructible<concur::intrusive_stack<int>>::value);
|
||||
EXPECT_FALSE(std::is_move_assignable<concur::intrusive_stack<int>>::value);
|
||||
}
|
||||
|
||||
TEST(intrusive_stack, push_one) {
|
||||
concur::intrusive_stack<int> s;
|
||||
concur::intrusive_stack<int>::node n;
|
||||
s.push(&n);
|
||||
EXPECT_FALSE(s.empty());
|
||||
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == &n);
|
||||
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == nullptr);
|
||||
}
|
||||
|
||||
TEST(intrusive_stack, push_many) {
|
||||
concur::intrusive_stack<int> s;
|
||||
concur::intrusive_stack<int>::node n1;
|
||||
concur::intrusive_stack<int>::node n2;
|
||||
concur::intrusive_stack<int>::node n3;
|
||||
s.push(&n1);
|
||||
s.push(&n2);
|
||||
s.push(&n3);
|
||||
EXPECT_FALSE(s.empty());
|
||||
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == &n3);
|
||||
EXPECT_TRUE(n3.next.load(std::memory_order_relaxed) == &n2);
|
||||
EXPECT_TRUE(n2.next.load(std::memory_order_relaxed) == &n1);
|
||||
EXPECT_TRUE(n1.next.load(std::memory_order_relaxed) == nullptr);
|
||||
}
|
||||
|
||||
TEST(intrusive_stack, push_same) {
|
||||
concur::intrusive_stack<int> s;
|
||||
concur::intrusive_stack<int>::node n;
|
||||
s.push(&n);
|
||||
s.push(&n);
|
||||
EXPECT_FALSE(s.empty());
|
||||
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == &n);
|
||||
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == &n);
|
||||
}
|
||||
|
||||
TEST(intrusive_stack, pop_empty) {
|
||||
concur::intrusive_stack<int> s;
|
||||
EXPECT_TRUE(s.pop() == nullptr);
|
||||
}
|
||||
|
||||
TEST(intrusive_stack, pop_one) {
|
||||
concur::intrusive_stack<int> s;
|
||||
concur::intrusive_stack<int>::node n;
|
||||
s.push(&n);
|
||||
EXPECT_TRUE(s.pop() == &n);
|
||||
EXPECT_TRUE(s.empty());
|
||||
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == nullptr);
|
||||
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == nullptr);
|
||||
}
|
||||
|
||||
TEST(intrusive_stack, pop_many) {
|
||||
concur::intrusive_stack<int> s;
|
||||
concur::intrusive_stack<int>::node n1;
|
||||
concur::intrusive_stack<int>::node n2;
|
||||
concur::intrusive_stack<int>::node n3;
|
||||
s.push(&n1);
|
||||
s.push(&n2);
|
||||
s.push(&n3);
|
||||
EXPECT_TRUE(s.pop() == &n3);
|
||||
EXPECT_TRUE(s.pop() == &n2);
|
||||
EXPECT_TRUE(s.pop() == &n1);
|
||||
EXPECT_TRUE(s.empty());
|
||||
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == nullptr);
|
||||
EXPECT_TRUE(n3.next.load(std::memory_order_relaxed) == &n2);
|
||||
EXPECT_TRUE(n2.next.load(std::memory_order_relaxed) == &n1);
|
||||
EXPECT_TRUE(n1.next.load(std::memory_order_relaxed) == nullptr);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user