7#include <userver/concurrent/impl/intrusive_hooks.hpp>
8#include <userver/concurrent/impl/tagged_ptr.hpp>
9#include <userver/utils/assert.hpp>
11USERVER_NAMESPACE_BEGIN
27template <
typename T,
typename HookExtractor>
28class IntrusiveStack final {
29 static_assert(std::is_empty_v<HookExtractor>);
32 constexpr IntrusiveStack() =
default;
34 IntrusiveStack(IntrusiveStack&&) =
delete;
35 IntrusiveStack& operator=(IntrusiveStack&&) =
delete;
37 void Push(T& node)
noexcept {
38 UASSERT_MSG(GetNext(node).load(std::memory_order_relaxed) ==
nullptr,
39 "This node is already contained in an IntrusiveStack");
41 NodeTaggedPtr expected = stack_head_.load();
43 GetNext(node).store(expected.GetDataPtr());
44 const NodeTaggedPtr desired(&node, expected.GetTag());
45 if (stack_head_.compare_exchange_weak(expected, desired)) {
51 T* TryPop()
noexcept {
52 NodeTaggedPtr expected = stack_head_.load();
54 T*
const expected_ptr = expected.GetDataPtr();
55 if (!expected_ptr)
return nullptr;
56 const NodeTaggedPtr desired(GetNext(*expected_ptr).load(),
57 expected.GetNextTag());
58 if (stack_head_.compare_exchange_weak(expected, desired)) {
60 GetNext(*expected_ptr).store(
nullptr, std::memory_order_relaxed);
66 template <
typename Func>
67 void WalkUnsafe(
const Func& func) {
71 template <
typename Func>
72 void WalkUnsafe(
const Func& func)
const {
73 DoWalk<
const T&>(func);
76 template <
typename DisposerFunc>
77 void DisposeUnsafe(
const DisposerFunc& disposer)
noexcept {
78 T* iter = stack_head_.load().GetDataPtr();
79 stack_head_.store(
nullptr);
81 T*
const old_iter = iter;
82 iter = GetNext(*iter).load();
87 std::size_t GetSizeUnsafe()
const noexcept {
89 WalkUnsafe([&](
auto& ) { ++size; });
94 using NodeTaggedPtr = TaggedPtr<T>;
96 static_assert(std::atomic<NodeTaggedPtr>::is_always_lock_free);
97 static_assert(std::has_unique_object_representations_v<NodeTaggedPtr>);
99 static std::atomic<T*>& GetNext(T& node)
noexcept {
100 SinglyLinkedHook<T>& hook = HookExtractor{}(node);
104 template <
typename U,
typename Func>
105 void DoWalk(
const Func& func)
const {
106 for (
auto* iter = stack_head_.load().GetDataPtr(); iter;
107 iter = GetNext(*iter).load()) {
108 func(
static_cast<U>(*iter));
112 std::atomic<NodeTaggedPtr> stack_head_{
nullptr};