Corium 1.1.0
High-Performance Zero-Heap C++20 MPSC Application Runtime
Loading...
Searching...
No Matches
SignalPolicies.hpp
Go to the documentation of this file.
1
7#pragma once
8
9#include <atomic>
10#include <chrono>
11#include <cstdint>
12
13#if defined(_WIN32) || defined(_WIN64) || defined(__unix__) || defined(__APPLE__) || (defined(_GLIBCXX_HAS_GTHREADS) && _GLIBCXX_HAS_GTHREADS) || defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
14#include <condition_variable>
15#include <mutex>
16#include <thread>
17#ifndef CORIUM_HAS_STD_MUTEX
18#define CORIUM_HAS_STD_MUTEX 1
19#endif
20#else
21#ifndef CORIUM_HAS_STD_MUTEX
22#define CORIUM_HAS_STD_MUTEX 0
23#endif
24#endif
25
26#ifdef __linux__
27#include <poll.h>
28#include <sys/eventfd.h>
29#include <unistd.h>
30#endif
31
32namespace corium {
33
36 using SimpleFn = void (*)();
37 using ContextFn = void (*)(void* arg);
38
39 ContextFn fn = nullptr;
40 void* arg = nullptr;
41
42 StaticCallback() = default;
43
44 /* implicit */ StaticCallback(SimpleFn simpleFn)
45 : fn(reinterpret_cast<ContextFn>(simpleFn))
46 {
47 if (simpleFn) {
48 // Helper trampoline for parameterless function pointers
49 fn = [](void* context) {
50 reinterpret_cast<SimpleFn>(context)();
51 };
52 arg = reinterpret_cast<void*>(simpleFn);
53 }
54 }
55
56 StaticCallback(ContextFn contextFn, void* contextArg)
57 : fn(contextFn), arg(contextArg)
58 {}
59
60 void operator()() const {
61 if (fn) {
62 fn(arg);
63 }
64 }
65
66 explicit operator bool() const noexcept {
67 return fn != nullptr;
68 }
69};
70
73public:
74 void setOnQueueNonEmpty(StaticCallback cb) noexcept { (void)cb; }
75 void signal() noexcept {}
76
77 template <typename Rep, typename Period>
78 void wait_for(const std::chrono::duration<Rep, Period>&) noexcept
79 {
80#if defined(__arm__) || defined(__aarch64__)
81 asm volatile("yield");
82#elif defined(__x86_64__)
83 __builtin_ia32_pause();
84#else
85 // zero-op spin yield
86#endif
87 }
88};
89
92public:
94 {
95#if CORIUM_HAS_STD_MUTEX
96 std::lock_guard<std::mutex> lock(_mutex);
97#endif
98 _callback = callback;
99 }
100
101 void signal()
102 {
103#if CORIUM_HAS_STD_MUTEX
104 std::lock_guard<std::mutex> lock(_mutex);
105 _hasEvents = true;
106 _cv.notify_one();
107#else
108 _hasEvents = true;
109#endif
110 if (_callback) {
111 _callback();
112 }
113 }
114
115 template <typename Rep, typename Period>
116 void wait_for(const std::chrono::duration<Rep, Period>& timeout)
117 {
118#if CORIUM_HAS_STD_MUTEX
119 std::unique_lock<std::mutex> lock(_mutex);
120 _cv.wait_for(lock, timeout, [this]() { return _hasEvents; });
121 _hasEvents = false;
122#else
123 (void)timeout;
124#if defined(__arm__) || defined(__aarch64__)
125 asm volatile("yield");
126#endif
127 _hasEvents = false;
128#endif
129 }
130
131private:
132 StaticCallback _callback;
133#if CORIUM_HAS_STD_MUTEX
134 std::mutex _mutex;
135 std::condition_variable _cv;
136#endif
137 bool _hasEvents = false;
138};
139
142public:
144 {
145 _userCallback = callback;
146 }
147
148 void signal()
149 {
150 _flag.store(true, std::memory_order_release);
151#if defined(__cpp_lib_atomic_wait) && __cpp_lib_atomic_wait >= 201907L
152 _flag.notify_one();
153#endif
154 if (_userCallback) {
155 _userCallback();
156 }
157 }
158
159 template <typename Rep, typename Period>
160 void wait_for(const std::chrono::duration<Rep, Period>& timeout)
161 {
162 if (_flag.load(std::memory_order_acquire)) {
163 _flag.store(false, std::memory_order_relaxed);
164 return;
165 }
166 auto deadline = std::chrono::steady_clock::now() + timeout;
167 while (!_flag.load(std::memory_order_acquire)) {
168 if (std::chrono::steady_clock::now() >= deadline) {
169 return;
170 }
171#if CORIUM_HAS_STD_MUTEX
172 std::this_thread::yield();
173#elif defined(__arm__) || defined(__aarch64__)
174 asm volatile("yield");
175#endif
176 }
177 _flag.store(false, std::memory_order_relaxed);
178 }
179
180private:
181 std::atomic<bool> _flag{false};
182 StaticCallback _userCallback;
183};
184
185#ifdef __linux__
187class EventFdSignalPolicy {
188public:
189 EventFdSignalPolicy()
190 {
191 _fd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
192 }
193
194 ~EventFdSignalPolicy()
195 {
196 if (_fd >= 0) {
197 ::close(_fd);
198 }
199 }
200
201 EventFdSignalPolicy(const EventFdSignalPolicy&) = delete;
202 EventFdSignalPolicy& operator=(const EventFdSignalPolicy&) = delete;
203
204 EventFdSignalPolicy(EventFdSignalPolicy&& rhs) noexcept : _fd(rhs._fd)
205 {
206 rhs._fd = -1;
207 }
208
209 EventFdSignalPolicy& operator=(EventFdSignalPolicy&& rhs) noexcept
210 {
211 if (this != &rhs) {
212 if (_fd >= 0) ::close(_fd);
213 _fd = rhs._fd;
214 rhs._fd = -1;
215 }
216 return *this;
217 }
218
219 void setOnQueueNonEmpty(StaticCallback callback)
220 {
221 _userCallback = callback;
222 }
223
224 void signal()
225 {
226 uint64_t val = 1;
227 [[maybe_unused]] auto res = ::write(_fd, &val, sizeof(val));
228 if (_userCallback) {
229 _userCallback();
230 }
231 }
232
233 template <typename Rep, typename Period>
234 void wait_for(const std::chrono::duration<Rep, Period>& timeout)
235 {
236 if (_fd < 0) return;
237 struct pollfd pfd;
238 pfd.fd = _fd;
239 pfd.events = POLLIN;
240 int timeoutMs = static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count());
241 int res = ::poll(&pfd, 1, timeoutMs);
242 if (res > 0 && (pfd.revents & POLLIN)) {
243 uint64_t val = 0;
244 [[maybe_unused]] auto bytes = ::read(_fd, &val, sizeof(val));
245 }
246 }
247
248 [[nodiscard]] int nativeHandle() const noexcept
249 {
250 return _fd;
251 }
252
253private:
254 int _fd = -1;
255 StaticCallback _userCallback;
256};
257#endif // __linux__
258
259} // namespace corium
Signal Policy using C++20 std::atomic::wait() / notify_one() for zero-mutex futex signaling.
Definition SignalPolicies.hpp:141
void setOnQueueNonEmpty(StaticCallback callback)
Definition SignalPolicies.hpp:143
void signal()
Definition SignalPolicies.hpp:148
void wait_for(const std::chrono::duration< Rep, Period > &timeout)
Definition SignalPolicies.hpp:160
Signal Policy invoking an edge-triggered callback on 0->1 transition when queue becomes non-empty.
Definition SignalPolicies.hpp:91
void setOnQueueNonEmpty(StaticCallback callback)
Definition SignalPolicies.hpp:93
void signal()
Definition SignalPolicies.hpp:101
void wait_for(const std::chrono::duration< Rep, Period > &timeout)
Definition SignalPolicies.hpp:116
Signal Policy for busy-spin / polling event loops (sub-microsecond latency, zero signaling cost).
Definition SignalPolicies.hpp:72
void signal() noexcept
Definition SignalPolicies.hpp:75
void wait_for(const std::chrono::duration< Rep, Period > &) noexcept
Definition SignalPolicies.hpp:78
void setOnQueueNonEmpty(StaticCallback cb) noexcept
Definition SignalPolicies.hpp:74
Definition Application.hpp:16
Lightweight non-allocating static callback wrapper (function pointer + optional context argument).
Definition SignalPolicies.hpp:35
StaticCallback(ContextFn contextFn, void *contextArg)
Definition SignalPolicies.hpp:56
void(*)() SimpleFn
Definition SignalPolicies.hpp:36
void(*)(void *arg) ContextFn
Definition SignalPolicies.hpp:37
void operator()() const
Definition SignalPolicies.hpp:60
StaticCallback(SimpleFn simpleFn)
Definition SignalPolicies.hpp:44
ContextFn fn
Definition SignalPolicies.hpp:39
void * arg
Definition SignalPolicies.hpp:40