Logger System 0.1.3
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
22#include <atomic>
23#include <memory>
24#include <array>
25#include <type_traits>
26
27namespace kcenon::logger::async {
28
34template<typename T, size_t Size>
36 static_assert((Size & (Size - 1)) == 0, "Size must be a power of 2");
37 static_assert(Size > 1, "Size must be greater than 1");
38
39public:
44 : head_{0}
45 , tail_{0} {
46 // Initialize cell sequence numbers for ABA prevention
47 for (size_t i = 0; i < Size; ++i) {
48 cells_[i].sequence.store(i, std::memory_order_relaxed);
49 }
50 }
51
56 T item;
57 while (dequeue(item)) {
58 // Clean up remaining items
59 }
60 }
61
67 bool enqueue(const T& item) {
68 return enqueue_impl(item);
69 }
70
76 bool enqueue(T&& item) {
77 return enqueue_impl(std::move(item));
78 }
79
85 bool dequeue(T& item) {
86 const size_t pos = tail_.load(std::memory_order_relaxed);
87 auto& cell = cells_[pos & mask_];
88
89 const size_t seq = cell.sequence.load(std::memory_order_acquire);
90 const size_t expected_seq = pos + 1;
91
92 if (seq != expected_seq) {
93 return false; // Queue is empty
94 }
95
96 item = std::move(cell.data);
97 cell.sequence.store(pos + mask_ + 1, std::memory_order_release);
98 tail_.store(pos + 1, std::memory_order_relaxed);
99
100 return true;
101 }
102
107 bool empty() const {
108 const size_t head_pos = head_.load(std::memory_order_acquire);
109 const size_t tail_pos = tail_.load(std::memory_order_acquire);
110 return head_pos == tail_pos;
111 }
112
117 bool full() const {
118 const size_t head_pos = head_.load(std::memory_order_acquire);
119 const size_t tail_pos = tail_.load(std::memory_order_acquire);
120 return ((head_pos + 1) & mask_) == (tail_pos & mask_);
121 }
122
127 size_t size() const {
128 const size_t head_pos = head_.load(std::memory_order_acquire);
129 const size_t tail_pos = tail_.load(std::memory_order_acquire);
130 return head_pos - tail_pos;
131 }
132
137 constexpr size_t capacity() const {
138 return Size;
139 }
140
141private:
142 static constexpr size_t mask_ = Size - 1;
143
147 static constexpr size_t cache_line_size = 64;
148
152 struct alignas(cache_line_size) cell {
153 std::atomic<size_t> sequence;
155
156 cell() : sequence{0} {}
157 };
158
165 template<typename U>
166 bool enqueue_impl(U&& item) {
167 const size_t pos = head_.load(std::memory_order_relaxed);
168 auto& cell = cells_[pos & mask_];
169
170 const size_t seq = cell.sequence.load(std::memory_order_acquire);
171 const size_t expected_seq = pos;
172
173 if (seq != expected_seq) {
174 return false; // Queue is full
175 }
176
177 cell.data = std::forward<U>(item);
178 cell.sequence.store(pos + 1, std::memory_order_release);
179 head_.store(pos + 1, std::memory_order_relaxed);
180
181 return true;
182 }
183
184 // Align to cache line boundaries to prevent false sharing
185 alignas(cache_line_size) std::atomic<size_t> head_;
186 alignas(cache_line_size) std::atomic<size_t> tail_;
187 alignas(cache_line_size) std::array<cell, Size> cells_;
188};
189
196template<typename T, size_t Size = 1024>
197std::unique_ptr<lockfree_spsc_queue<T, Size>> make_lockfree_queue() {
198 return std::make_unique<lockfree_spsc_queue<T, Size>>();
199}
200
207template<typename T, size_t Size>
209 // TODO: Implement if multi-producer scenarios are needed
210 static_assert(sizeof(T) == 0, "MPMC queue not yet implemented");
211};
212
213} // 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.