mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
Add intrusive_stack
This commit is contained in:
parent
d260897b16
commit
e83bd8f874
66
include/libipc/concur/intrusive_stack.h
Normal file
66
include/libipc/concur/intrusive_stack.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* \file libconcur/intrusive_stack.h
|
||||||
|
* \author mutouyun (orz@orzz.org)
|
||||||
|
* \brief Define concurrent intrusive stack.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace concur {
|
||||||
|
|
||||||
|
/// \brief Intrusive stack node.
|
||||||
|
/// \tparam T The type of the value.
|
||||||
|
template <typename T>
|
||||||
|
struct intrusive_node {
|
||||||
|
T value;
|
||||||
|
std::atomic<intrusive_node *> next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Intrusive stack.
|
||||||
|
/// \tparam T The type of the value.
|
||||||
|
/// \tparam Node The type of the node.
|
||||||
|
template <typename T, typename Node = intrusive_node<T>>
|
||||||
|
class intrusive_stack {
|
||||||
|
public:
|
||||||
|
using node = Node;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concur
|
||||||
|
} // namespace ipc
|
||||||
@ -20,6 +20,7 @@ file(GLOB SRC_FILES
|
|||||||
${LIBIPC_PROJECT_DIR}/test/test_*.cpp
|
${LIBIPC_PROJECT_DIR}/test/test_*.cpp
|
||||||
${LIBIPC_PROJECT_DIR}/test/imp/*.cpp
|
${LIBIPC_PROJECT_DIR}/test/imp/*.cpp
|
||||||
${LIBIPC_PROJECT_DIR}/test/mem/*.cpp
|
${LIBIPC_PROJECT_DIR}/test/mem/*.cpp
|
||||||
|
${LIBIPC_PROJECT_DIR}/test/concur/*.cpp
|
||||||
# ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp
|
# ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp
|
||||||
)
|
)
|
||||||
file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/test_*.h)
|
file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/test_*.h)
|
||||||
|
|||||||
103
test/concur/test_concur_intrusive_stack.cpp
Normal file
103
test/concur/test_concur_intrusive_stack.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#define private public
|
||||||
|
#include "libipc/concur/intrusive_stack.h"
|
||||||
|
|
||||||
|
using namespace ipc;
|
||||||
|
|
||||||
|
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{123};
|
||||||
|
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);
|
||||||
|
EXPECT_EQ(n.value, 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(intrusive_stack, push_many) {
|
||||||
|
concur::intrusive_stack<int> s;
|
||||||
|
concur::intrusive_stack<int>::node n1{111111};
|
||||||
|
concur::intrusive_stack<int>::node n2{222222};
|
||||||
|
concur::intrusive_stack<int>::node n3{333333};
|
||||||
|
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);
|
||||||
|
EXPECT_EQ(n1.value, 111111);
|
||||||
|
EXPECT_EQ(n2.value, 222222);
|
||||||
|
EXPECT_EQ(n3.value, 333333);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(intrusive_stack, push_same) {
|
||||||
|
concur::intrusive_stack<int> s;
|
||||||
|
concur::intrusive_stack<int>::node n{321};
|
||||||
|
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);
|
||||||
|
EXPECT_EQ(n.value, 321);
|
||||||
|
}
|
||||||
|
|
||||||
|
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{112233};
|
||||||
|
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);
|
||||||
|
EXPECT_EQ(n.value, 112233);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(intrusive_stack, pop_many) {
|
||||||
|
concur::intrusive_stack<int> s;
|
||||||
|
concur::intrusive_stack<int>::node n1{111111};
|
||||||
|
concur::intrusive_stack<int>::node n2{222222};
|
||||||
|
concur::intrusive_stack<int>::node n3{333333};
|
||||||
|
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);
|
||||||
|
EXPECT_EQ(n1.value, 111111);
|
||||||
|
EXPECT_EQ(n2.value, 222222);
|
||||||
|
EXPECT_EQ(n3.value, 333333);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user