MayaFlux 0.1.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ServerThread.hpp
Go to the documentation of this file.
1#pragma once
2
3#if __has_include(<stop_token>)
4#include <stop_token>
5#endif
6
7/**
8 * @file ServerThread.hpp
9 * @brief Platform-specific threading wrapper for Server class
10 *
11 * This header provides a surgical fix for std::jthread issues on specific platforms
12 * (notably Apple Clang 15 shipped with Xcode 15.x) WITHOUT disabling other C++23 features.
13 *
14 * Detection Strategy:
15 * 1. Check if __cpp_lib_jthread feature test macro indicates support
16 * 2. On macOS, check Apple Clang compiler version specifically
17 * 3. Apple Clang 15.x (15000000 - 15999999) has known jthread issues despite claiming support
18 * 4. Fallback to std::thread + atomic flag only when jthread is actually broken
19 *
20 * This does NOT affect any other C++23/C++20 features like:
21 * - std::expected
22 * - Coroutines (std::coroutine_handle)
23 * - std::atomic_ref
24 * - Structured bindings
25 * - Concepts
26 * - Ranges
27 */
28
29#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L
30
31#if defined(__apple_build_version__)
32// Apple Clang: need to verify version
33// Apple Clang version format: major * 10000000 + minor * 100000 + patch
34// Xcode 15.0 = Apple Clang 15.0.0 = 15000000
35// Xcode 15.4 = Apple Clang 15.0.0 = 15000309 (example)
36// Xcode 16.0 = Apple Clang 16.0.0 = 16000000
37
38#if __apple_build_version__ >= 15000000 && __apple_build_version__ < 16000000
39// Apple Clang 15.x series: known to have jthread issues
40// Despite feature test macro claiming support, implementation is buggy
41#define MAYAFLUX_JTHREAD_BROKEN 1
42#pragma message("ServerThread: Detected Apple Clang 15.x - using std::thread fallback for jthread")
43#else
44// Apple Clang < 15 or >= 16: assume working jthread
45#define MAYAFLUX_JTHREAD_WORKING 1
46#endif
47#else
48// Non-Apple compilers: trust the feature test macro
49#define MAYAFLUX_JTHREAD_WORKING 1
50#endif
51#else
52// Feature test macro says jthread is not available
53#define MAYAFLUX_JTHREAD_BROKEN 1
54#pragma message("ServerThread: std::jthread not available - using std::thread fallback")
55#endif
56
57namespace Lila {
58
59#ifdef MAYAFLUX_JTHREAD_WORKING
60
61/**
62 * @brief Modern std::jthread-based thread wrapper
63 *
64 * Direct wrapper around std::jthread for platforms with full, working support.
65 * Provides zero-overhead abstraction - compiles to identical code as raw std::jthread.
66 */
67class ServerThread {
68public:
69 using Callback = std::function<void(const std::stop_token&)>;
70
71 ServerThread() = default;
72
73 explicit ServerThread(Callback callback)
74 : m_thread(std::move(callback))
75 {
76 }
77
78 ServerThread(ServerThread&&) noexcept = default;
79 ServerThread& operator=(ServerThread&&) noexcept = default;
80
81 ServerThread(const ServerThread&) = delete;
82 ServerThread& operator=(const ServerThread&) = delete;
83
84 void request_stop() noexcept
85 {
87 }
88
89 void join()
90 {
91 if (m_thread.joinable()) {
92 m_thread.join();
93 }
94 }
95
96 [[nodiscard]] bool joinable() const noexcept
97 {
98 return m_thread.joinable();
99 }
100
101private:
102 std::jthread m_thread;
103};
104
105#else // MAYAFLUX_JTHREAD_BROKEN
106
107/**
108 * @brief Fallback std::thread-based thread wrapper with manual stop signaling
109 *
110 * Compatible implementation for platforms where std::jthread is unavailable or broken.
111 * Provides identical interface using std::thread + std::atomic<bool> for stop signaling.
112 *
113 * Implementation Notes:
114 * - StopToken provides API-compatible interface with std::stop_token
115 * - Uses shared_ptr<atomic<bool>> to ensure stop flag outlives thread
116 * - Memory ordering: acquire on read, release on write (matches std::stop_token semantics)
117 * - Minimal overhead: one atomic load per loop iteration (~1-2 CPU cycles)
118 */
120public:
121 /**
122 * @brief API-compatible stop token for platforms without std::stop_token
123 *
124 * Mimics std::stop_token's stop_requested() interface.
125 */
126 class StopToken {
127 public:
128 explicit StopToken(const std::atomic<bool>* stop_flag)
129 : m_stop_flag(stop_flag)
130 {
131 }
132
133 [[nodiscard]] bool stop_requested() const noexcept
134 {
135 return m_stop_flag && m_stop_flag->load(std::memory_order_acquire);
136 }
137
138 private:
139 const std::atomic<bool>* m_stop_flag { nullptr };
140 };
141
142 using Callback = std::function<void(const StopToken&)>;
143
144 ServerThread() = default;
145
146 explicit ServerThread(Callback callback)
147 : m_stop_flag(std::make_shared<std::atomic<bool>>(false))
148 {
149 // Capture stop_flag by value (shared_ptr) to ensure it outlives the thread
150 // critical: the thread lambda must hold a reference to keep the flag alive
151 auto stop_flag = m_stop_flag;
152
153 m_thread = std::thread([callback = std::move(callback), stop_flag]() {
154 StopToken token(stop_flag.get());
155 callback(token);
156 });
157 }
158
159 ServerThread(ServerThread&& other) noexcept
160 : m_thread(std::move(other.m_thread))
161 , m_stop_flag(std::move(other.m_stop_flag))
162 {
163 }
164
166 {
167 if (this != &other) {
168 if (m_thread.joinable()) {
169 request_stop();
170 m_thread.join();
171 }
172
173 m_thread = std::move(other.m_thread);
174 m_stop_flag = std::move(other.m_stop_flag);
175 }
176 return *this;
177 }
178
179 ServerThread(const ServerThread&) = delete;
181
183 {
184 // Automatically join on destruction (mimics std::jthread behavior)
185 if (m_thread.joinable()) {
186 request_stop();
187 m_thread.join();
188 }
189 }
190
191 void request_stop() noexcept
192 {
193 if (m_stop_flag) {
194 m_stop_flag->store(true, std::memory_order_release);
195 }
196 }
197
198 void join()
199 {
200 if (m_thread.joinable()) {
201 m_thread.join();
202 }
203 }
204
205 [[nodiscard]] bool joinable() const noexcept
206 {
207 return m_thread.joinable();
208 }
209
210private:
211 std::thread m_thread;
212 std::shared_ptr<std::atomic<bool>> m_stop_flag;
213};
214
215#endif // MAYAFLUX_JTHREAD_BROKEN / MAYAFLUX_JTHREAD_WORKING
216
217} // namespace Lila
static MayaFlux::Nodes::ProcessingToken token
Definition Timers.cpp:8
bool stop_requested() const noexcept
const std::atomic< bool > * m_stop_flag
StopToken(const std::atomic< bool > *stop_flag)
API-compatible stop token for platforms without std::stop_token.
ServerThread()=default
void request_stop() noexcept
std::shared_ptr< std::atomic< bool > > m_stop_flag
ServerThread(ServerThread &&other) noexcept
ServerThread & operator=(const ServerThread &)=delete
ServerThread(Callback callback)
std::function< void(const StopToken &)> Callback
ServerThread & operator=(ServerThread &&other) noexcept
ServerThread(const ServerThread &)=delete
bool joinable() const noexcept
Fallback std::thread-based thread wrapper with manual stop signaling.