Logger System 1.0.0
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
lockfree_queue.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
5#pragma once
6
28#include <atomic>
29#include <memory>
30#include <array>
31#include <type_traits>
32
33namespace kcenon::logger::async {
34
40template<typename T, size_t Size>
42 static_assert((Size & (Size - 1)) == 0, "Size must be a power of 2");
43 static_assert(Size > 1, "Size must be greater than 1");
44
45public:
50 : head_{0}
51 , tail_{0} {
52 // Initialize cell sequence numbers for ABA prevention
53 for (size_t i = 0; i < Size; ++i) {
54 cells_[i].sequence.store(i, std::memory_order_relaxed);
55 }
56 }
57
62 T item;
63 while (dequeue(item)) {
64 // Clean up remaining items
65 }
66 }
67
73 bool enqueue(const T& item) {
74 return enqueue_impl(item);
75 }
76
82 bool enqueue(T&& item) {
83 return enqueue_impl(std::move(item));
84 }
85
91 bool dequeue(T& item) {
92 const size_t pos = tail_.load(std::memory_order_relaxed);
93 auto& cell = cells_[pos & mask_];
94
95 const size_t seq = cell.sequence.load(std::memory_order_acquire);
96 const size_t expected_seq = pos + 1;
97
98 if (seq != expected_seq) {
99 return false; // Queue is empty
100 }
101
102 item = std::move(cell.data);
103 cell.sequence.store(pos + mask_ + 1, std::memory_order_release);
104 tail_.store(pos + 1, std::memory_order_relaxed);
105
106 return true;
107 }
108
113 bool empty() const {
114 const size_t head_pos = head_.load(std::memory_order_acquire);
115 const size_t tail_pos = tail_.load(std::memory_order_acquire);
116 return head_pos == tail_pos;
117 }
118
123 bool full() const {
124 const size_t head_pos = head_.load(std::memory_order_acquire);
125 const size_t tail_pos = tail_.load(std::memory_order_acquire);
126 return ((head_pos + 1) & mask_) == (tail_pos & mask_);
127 }
128
133 size_t size() const {
134 const size_t head_pos = head_.load(std::memory_order_acquire);
135 const size_t tail_pos = tail_.load(std::memory_order_acquire);
136 return head_pos - tail_pos;
137 }
138
143 constexpr size_t capacity() const {
144 return Size;
145 }
146
147private:
148 static constexpr size_t mask_ = Size - 1;
149
153 static constexpr size_t cache_line_size = 64;
154
158 struct alignas(cache_line_size) cell {
159 std::atomic<size_t> sequence;
161
162 cell() : sequence{0} {}
163 };
164
171 template<typename U>
172 bool enqueue_impl(U&& item) {
173 const size_t pos = head_.load(std::memory_order_relaxed);
174 auto& cell = cells_[pos & mask_];
175
176 const size_t seq = cell.sequence.load(std::memory_order_acquire);
177 const size_t expected_seq = pos;
178
179 if (seq != expected_seq) {
180 return false; // Queue is full
181 }
182
183 cell.data = std::forward<U>(item);
184 cell.sequence.store(pos + 1, std::memory_order_release);
185 head_.store(pos + 1, std::memory_order_relaxed);
186
187 return true;
188 }
189
190 // Align to cache line boundaries to prevent false sharing
191 alignas(cache_line_size) std::atomic<size_t> head_;
192 alignas(cache_line_size) std::atomic<size_t> tail_;
193 alignas(cache_line_size) std::array<cell, Size> cells_;
194};
195
202template<typename T, size_t Size = 1024>
203std::unique_ptr<lockfree_spsc_queue<T, Size>> make_lockfree_queue() {
204 return std::make_unique<lockfree_spsc_queue<T, Size>>();
205}
206
213template<typename T, size_t Size>
215 // TODO: Implement if multi-producer scenarios are needed
216 static_assert(sizeof(T) == 0, "MPMC queue not yet implemented");
217};
218
219} // namespace kcenon::logger::async
Multi-producer multi-consumer lock-free queue (for future use)
Lock-free single-producer single-consumer queue.
bool empty() const
Check if queue is empty.
bool enqueue(T &&item)
Enqueue an item using move semantics (producer side)
size_t size() const
Get approximate queue size.
bool enqueue(const T &item)
Enqueue an item (producer side)
static constexpr size_t cache_line_size
Cache line size for padding.
bool dequeue(T &item)
Dequeue an item (consumer side)
bool full() const
Check if queue is full.
constexpr size_t capacity() const
Get queue capacity.
bool enqueue_impl(U &&item)
Implementation for enqueue operations.
std::unique_ptr< lockfree_spsc_queue< T, Size > > make_lockfree_queue()
Factory function to create a lock-free queue.
Cell structure with sequence number for ABA prevention.