Skip to content

Commit ad21fac

Browse files
committed
Add intrusive_stack
1 parent 6b03760 commit ad21fac

File tree

3 files changed

+170
-0
lines changed

3 files changed

+170
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* \file libconcur/intrusive_stack.h
3+
* \author mutouyun ([email protected])
4+
* \brief Define concurrent intrusive stack.
5+
*/
6+
#pragma once
7+
8+
#include <atomic>
9+
10+
namespace ipc {
11+
namespace concur {
12+
13+
/// \brief Intrusive stack node.
14+
/// \tparam T The type of the value.
15+
template <typename T>
16+
struct intrusive_node {
17+
T value;
18+
std::atomic<intrusive_node *> next;
19+
};
20+
21+
/// \brief Intrusive stack.
22+
/// \tparam T The type of the value.
23+
/// \tparam Node The type of the node.
24+
template <typename T, typename Node = intrusive_node<T>>
25+
class intrusive_stack {
26+
public:
27+
using node = Node;
28+
29+
private:
30+
std::atomic<node *> top_{nullptr};
31+
32+
public:
33+
intrusive_stack(intrusive_stack const &) = delete;
34+
intrusive_stack(intrusive_stack &&) = delete;
35+
intrusive_stack &operator=(intrusive_stack const &) = delete;
36+
intrusive_stack &operator=(intrusive_stack &&) = delete;
37+
38+
constexpr intrusive_stack() noexcept = default;
39+
40+
bool empty() const noexcept {
41+
return top_.load(std::memory_order_acquire) == nullptr;
42+
}
43+
44+
void push(node *n) noexcept {
45+
node *old_top = top_.load(std::memory_order_acquire);
46+
do {
47+
n->next.store(old_top, std::memory_order_relaxed);
48+
} while (!top_.compare_exchange_weak(old_top, n, std::memory_order_release
49+
, std::memory_order_acquire));
50+
}
51+
52+
node *pop() noexcept {
53+
node *old_top = top_.load(std::memory_order_acquire);
54+
do {
55+
if (old_top == nullptr) {
56+
return nullptr;
57+
}
58+
} while (!top_.compare_exchange_weak(old_top, old_top->next.load(std::memory_order_relaxed)
59+
, std::memory_order_release
60+
, std::memory_order_acquire));
61+
return old_top;
62+
}
63+
};
64+
65+
} // namespace concur
66+
} // namespace ipc

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ file(GLOB SRC_FILES
1919
${LIBIPC_PROJECT_DIR}/test/*.cpp
2020
${LIBIPC_PROJECT_DIR}/test/imp/*.cpp
2121
${LIBIPC_PROJECT_DIR}/test/mem/*.cpp
22+
${LIBIPC_PROJECT_DIR}/test/concur/*.cpp
2223
# ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp
2324
)
2425
file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/*.h)
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
2+
#include "test.h"
3+
4+
#define private public
5+
#include "libipc/concur/intrusive_stack.h"
6+
7+
using namespace ipc;
8+
9+
TEST(intrusive_stack, construct) {
10+
concur::intrusive_stack<int> s;
11+
EXPECT_TRUE(s.empty());
12+
}
13+
14+
TEST(intrusive_stack, construct_node) {
15+
concur::intrusive_stack<int>::node n{};
16+
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == nullptr);
17+
}
18+
19+
TEST(intrusive_stack, copyable) {
20+
EXPECT_FALSE(std::is_copy_constructible<concur::intrusive_stack<int>>::value);
21+
EXPECT_FALSE(std::is_copy_assignable<concur::intrusive_stack<int>>::value);
22+
}
23+
24+
TEST(intrusive_stack, moveable) {
25+
EXPECT_FALSE(std::is_move_constructible<concur::intrusive_stack<int>>::value);
26+
EXPECT_FALSE(std::is_move_assignable<concur::intrusive_stack<int>>::value);
27+
}
28+
29+
TEST(intrusive_stack, push_one) {
30+
concur::intrusive_stack<int> s;
31+
concur::intrusive_stack<int>::node n{123};
32+
s.push(&n);
33+
EXPECT_FALSE(s.empty());
34+
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == &n);
35+
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == nullptr);
36+
EXPECT_EQ(n.value, 123);
37+
}
38+
39+
TEST(intrusive_stack, push_many) {
40+
concur::intrusive_stack<int> s;
41+
concur::intrusive_stack<int>::node n1{111111};
42+
concur::intrusive_stack<int>::node n2{222222};
43+
concur::intrusive_stack<int>::node n3{333333};
44+
s.push(&n1);
45+
s.push(&n2);
46+
s.push(&n3);
47+
EXPECT_FALSE(s.empty());
48+
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == &n3);
49+
EXPECT_TRUE(n3.next.load(std::memory_order_relaxed) == &n2);
50+
EXPECT_TRUE(n2.next.load(std::memory_order_relaxed) == &n1);
51+
EXPECT_TRUE(n1.next.load(std::memory_order_relaxed) == nullptr);
52+
EXPECT_EQ(n1.value, 111111);
53+
EXPECT_EQ(n2.value, 222222);
54+
EXPECT_EQ(n3.value, 333333);
55+
}
56+
57+
TEST(intrusive_stack, push_same) {
58+
concur::intrusive_stack<int> s;
59+
concur::intrusive_stack<int>::node n{321};
60+
s.push(&n);
61+
s.push(&n);
62+
EXPECT_FALSE(s.empty());
63+
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == &n);
64+
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == &n);
65+
EXPECT_EQ(n.value, 321);
66+
}
67+
68+
TEST(intrusive_stack, pop_empty) {
69+
concur::intrusive_stack<int> s;
70+
EXPECT_TRUE(s.pop() == nullptr);
71+
}
72+
73+
TEST(intrusive_stack, pop_one) {
74+
concur::intrusive_stack<int> s;
75+
concur::intrusive_stack<int>::node n{112233};
76+
s.push(&n);
77+
EXPECT_TRUE(s.pop() == &n);
78+
EXPECT_TRUE(s.empty());
79+
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == nullptr);
80+
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == nullptr);
81+
EXPECT_EQ(n.value, 112233);
82+
}
83+
84+
TEST(intrusive_stack, pop_many) {
85+
concur::intrusive_stack<int> s;
86+
concur::intrusive_stack<int>::node n1{111111};
87+
concur::intrusive_stack<int>::node n2{222222};
88+
concur::intrusive_stack<int>::node n3{333333};
89+
s.push(&n1);
90+
s.push(&n2);
91+
s.push(&n3);
92+
EXPECT_TRUE(s.pop() == &n3);
93+
EXPECT_TRUE(s.pop() == &n2);
94+
EXPECT_TRUE(s.pop() == &n1);
95+
EXPECT_TRUE(s.empty());
96+
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == nullptr);
97+
EXPECT_TRUE(n3.next.load(std::memory_order_relaxed) == &n2);
98+
EXPECT_TRUE(n2.next.load(std::memory_order_relaxed) == &n1);
99+
EXPECT_TRUE(n1.next.load(std::memory_order_relaxed) == nullptr);
100+
EXPECT_EQ(n1.value, 111111);
101+
EXPECT_EQ(n2.value, 222222);
102+
EXPECT_EQ(n3.value, 333333);
103+
}

0 commit comments

Comments
 (0)