Corium 1.1.0
High-Performance Zero-Heap C++20 MPSC Application Runtime
Loading...
Searching...
No Matches
ShmMpscQueue.hpp
Go to the documentation of this file.
1
7#pragma once
8
9#include <atomic>
10#include <cassert>
11#include <cstddef>
12#include <cstdint>
13#include <cstring>
14#include <new>
15#include <type_traits>
16#include <utility>
17
18namespace corium::ipc {
19
20constexpr uint32_t CORIUM_SHM_MAGIC = 0x434F5249; // "CORI"
21constexpr uint32_t CORIUM_SHM_VERSION = 1;
22
27template <typename T, std::size_t Capacity = 256>
29 static_assert((Capacity & (Capacity - 1)) == 0, "Capacity must be a power of 2");
30 static_assert(Capacity >= 2, "Capacity must be at least 2");
31 static_assert(std::is_trivially_copyable_v<T>, "ShmMpscQueue value type T must be trivially copyable for shared memory safety.");
32
33public:
34 static constexpr std::size_t BufferCapacity = Capacity;
35 static constexpr std::size_t BufferMask = Capacity - 1;
36
37 struct Cell {
38 alignas(64) std::atomic<std::size_t> sequence{0};
39 alignas(alignof(T)) uint8_t storage[sizeof(T)]{};
40
41 [[nodiscard]] T* ptr() noexcept
42 {
43 return reinterpret_cast<T*>(storage);
44 }
45
46 [[nodiscard]] const T* ptr() const noexcept
47 {
48 return reinterpret_cast<const T*>(storage);
49 }
50 };
51
52 struct Layout {
55 uint32_t capacity{Capacity};
56 uint32_t elementSize{sizeof(T)};
57
58 alignas(64) std::atomic<std::size_t> enqueuePos{0};
59 alignas(64) std::atomic<std::size_t> dequeuePos{0};
60
61 Cell cells[Capacity];
62 };
63
64 ShmMpscQueue() = default;
65
67 explicit ShmMpscQueue(void* mappedAddress, bool initializeMemory = false) noexcept
68 {
69 bind(mappedAddress, initializeMemory);
70 }
71
75 void bind(void* mappedAddress, bool initializeMemory = false) noexcept
76 {
77 _layout = static_cast<Layout*>(mappedAddress);
78 if (_layout && initializeMemory) {
79 _layout->magic = CORIUM_SHM_MAGIC;
80 _layout->version = CORIUM_SHM_VERSION;
81 _layout->capacity = Capacity;
82 _layout->elementSize = sizeof(T);
83 _layout->enqueuePos.store(0, std::memory_order_relaxed);
84 _layout->dequeuePos.store(0, std::memory_order_relaxed);
85
86 for (std::size_t i = 0; i < Capacity; ++i) {
87 _layout->cells[i].sequence.store(i, std::memory_order_relaxed);
88 }
89 }
90 }
91
93 [[nodiscard]] static constexpr std::size_t requiredMemorySize() noexcept
94 {
95 return sizeof(Layout);
96 }
97
99 [[nodiscard]] bool isValid() const noexcept
100 {
101 if (!_layout) {
102 return false;
103 }
104 return _layout->magic == CORIUM_SHM_MAGIC &&
105 _layout->version == CORIUM_SHM_VERSION &&
106 _layout->capacity == Capacity &&
107 _layout->elementSize == sizeof(T);
108 }
109
113 template <typename U>
114 bool tryPush(U&& item) noexcept
115 {
116 if (!_layout) {
117 return false;
118 }
119
120 Cell* cell = nullptr;
121 std::size_t pos = _layout->enqueuePos.load(std::memory_order_relaxed);
122
123 for (;;) {
124 cell = &_layout->cells[pos & BufferMask];
125 const std::size_t seq = cell->sequence.load(std::memory_order_acquire);
126 const intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos);
127
128 if (dif == 0) {
129 if (_layout->enqueuePos.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) {
130 break;
131 }
132 } else if (dif < 0) {
133 // Buffer is full
134 return false;
135 } else {
136 pos = _layout->enqueuePos.load(std::memory_order_relaxed);
137 }
138 }
139
140 // Construct / copy item into cell storage
141 if constexpr (std::is_trivially_copyable_v<T>) {
142 std::memcpy(cell->storage, &item, sizeof(T));
143 } else {
144 new (cell->storage) T(std::forward<U>(item));
145 }
146
147 cell->sequence.store(pos + 1, std::memory_order_release);
148 return true;
149 }
150
154 bool tryPop(T& outItem) noexcept
155 {
156 if (!_layout) {
157 return false;
158 }
159
160 std::size_t pos = _layout->dequeuePos.load(std::memory_order_relaxed);
161 Cell* cell = &_layout->cells[pos & BufferMask];
162 const std::size_t seq = cell->sequence.load(std::memory_order_acquire);
163 const intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos + 1);
164
165 if (dif < 0) {
166 // Buffer is empty
167 return false;
168 }
169
170 if constexpr (std::is_trivially_copyable_v<T>) {
171 std::memcpy(&outItem, cell->storage, sizeof(T));
172 } else {
173 outItem = std::move(*cell->ptr());
174 cell->ptr()->~T();
175 }
176
177 cell->sequence.store(pos + Capacity, std::memory_order_release);
178 _layout->dequeuePos.store(pos + 1, std::memory_order_relaxed);
179 return true;
180 }
181
183 [[nodiscard]] bool empty() const noexcept
184 {
185 if (!_layout) return true;
186 const std::size_t pos = _layout->dequeuePos.load(std::memory_order_relaxed);
187 const Cell* cell = &_layout->cells[pos & BufferMask];
188 const std::size_t seq = cell->sequence.load(std::memory_order_acquire);
189 return static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos + 1) < 0;
190 }
191
193 [[nodiscard]] constexpr std::size_t capacity() const noexcept
194 {
195 return Capacity;
196 }
197
198private:
199 Layout* _layout{nullptr};
200};
201
202} // namespace corium::ipc
Lock-free, zero-allocation multi-producer single-consumer ring buffer layout for shared memory....
Definition ShmMpscQueue.hpp:28
static constexpr std::size_t BufferCapacity
Definition ShmMpscQueue.hpp:34
void bind(void *mappedAddress, bool initializeMemory=false) noexcept
Bind to a mapped shared memory region.
Definition ShmMpscQueue.hpp:75
bool empty() const noexcept
Check if queue is currently empty.
Definition ShmMpscQueue.hpp:183
bool tryPush(U &&item) noexcept
Lock-free push into shared memory queue (multi-producer safe).
Definition ShmMpscQueue.hpp:114
static constexpr std::size_t requiredMemorySize() noexcept
Required byte size of the shared memory layout.
Definition ShmMpscQueue.hpp:93
ShmMpscQueue(void *mappedAddress, bool initializeMemory=false) noexcept
Construct queue bound to a mapped shared memory address.
Definition ShmMpscQueue.hpp:67
bool tryPop(T &outItem) noexcept
Lock-free pop from shared memory queue (single-consumer safe).
Definition ShmMpscQueue.hpp:154
static constexpr std::size_t BufferMask
Definition ShmMpscQueue.hpp:35
constexpr std::size_t capacity() const noexcept
Get configured capacity of the queue.
Definition ShmMpscQueue.hpp:193
bool isValid() const noexcept
Validate that the mapped shared memory contains a compatible ShmMpscQueue header.
Definition ShmMpscQueue.hpp:99
Definition DomainSocket.hpp:35
constexpr uint32_t CORIUM_SHM_MAGIC
Definition ShmMpscQueue.hpp:20
constexpr uint32_t CORIUM_SHM_VERSION
Definition ShmMpscQueue.hpp:21
Definition ShmMpscQueue.hpp:37
std::atomic< std::size_t > sequence
Definition ShmMpscQueue.hpp:38
T * ptr() noexcept
Definition ShmMpscQueue.hpp:41
uint8_t storage[sizeof(T)]
Definition ShmMpscQueue.hpp:39
const T * ptr() const noexcept
Definition ShmMpscQueue.hpp:46
Definition ShmMpscQueue.hpp:52
std::atomic< std::size_t > enqueuePos
Definition ShmMpscQueue.hpp:58
std::atomic< std::size_t > dequeuePos
Definition ShmMpscQueue.hpp:59
uint32_t elementSize
Definition ShmMpscQueue.hpp:56
uint32_t version
Definition ShmMpscQueue.hpp:54
Cell cells[Capacity]
Definition ShmMpscQueue.hpp:61
uint32_t capacity
Definition ShmMpscQueue.hpp:55
uint32_t magic
Definition ShmMpscQueue.hpp:53