2#include <Process/ExecutionContext.hpp>
3#include <Process/ExecutionTransaction.hpp>
7#include <boost/pfr.hpp>
9#include <avnd/common/function_reflection.hpp>
10#include <avnd/common/tag.hpp>
11#include <avnd/concepts/message_bus.hpp>
27template <
typename R,
typename Arg>
45 bool request()
noexcept {
return !m_pending.exchange(
true, std::memory_order_acq_rel); }
46 void cancel()
noexcept { m_pending.store(
false, std::memory_order_release); }
47 void clear()
noexcept { m_pending.store(
false, std::memory_order_release); }
53 m_parked.store(
true, std::memory_order_relaxed);
56 bool unpark() noexcept {
return m_parked.exchange(
false, std::memory_order_relaxed); }
59 std::atomic_bool m_pending{
false};
60 std::atomic_bool m_parked{
false};
75 void add(std::function<
bool()> f) { m_retries.push_back(std::move(f)); }
78 void park() noexcept { m_parked =
true; }
83 if(!std::exchange(m_parked,
false))
85 for(
auto& f : m_retries)
91 std::vector<std::function<bool()>> m_retries;
104template <
typename T, std::
size_t N = 16>
107 static_assert(N > 0 && (N & (N - 1)) == 0);
109 bool push(T&& msg)
noexcept(std::is_nothrow_move_assignable_v<T>)
111 const auto w = m_write.load(std::memory_order_relaxed);
112 if(w - m_read.load(std::memory_order_acquire) >= N)
114 m_dropped.fetch_add(1, std::memory_order_relaxed);
117 m_slots[w % N] = std::move(msg);
118 m_write.store(w + 1, std::memory_order_release);
123 template <
typename F>
127 auto r = m_read.load(std::memory_order_relaxed);
128 const auto w = m_write.load(std::memory_order_acquire);
130 for(; r != w; ++r, ++n)
132 T msg = std::move(m_slots[r % N]);
133 m_read.store(r + 1, std::memory_order_release);
139 std::size_t dropped() const noexcept
141 return m_dropped.load(std::memory_order_relaxed);
145 std::array<T, N> m_slots{};
146 std::atomic<std::size_t> m_write{0};
147 std::atomic<std::size_t> m_read{0};
148 std::atomic<std::size_t> m_dropped{0};
163 template <
typename F>
164 void for_each_slot(F&& f)
166 for(
auto& s : m_slots)
170 T& back()
noexcept {
return m_slots[m_back]; }
172 void publish()
noexcept
174 m_back = m_middle.exchange(m_back | fresh, std::memory_order_acq_rel) & index_mask;
182 if(!(m_middle.load(std::memory_order_acquire) & fresh))
184 m_front = m_middle.exchange(m_front, std::memory_order_acq_rel) & index_mask;
185 return &m_slots[m_front];
189 static constexpr int fresh = 4;
190 static constexpr int index_mask = 3;
191 std::array<T, 3> m_slots{};
192 std::atomic<int> m_middle{1};
201 template <
typename F>
202 void operator()(
const F& f)
204 if constexpr(std::is_arithmetic_v<F>)
205 r.stream().stream << f;
206 else if constexpr(std::is_aggregate_v<F>)
207 avnd::pfr::for_each_field(f, *
this);
208 else if constexpr(avnd::list_ish<F>)
210 r.stream().stream << (int64_t)std::ssize(f);
211 for(
const auto& val : f)
220 template <
typename... Args>
221 void operator()(
const std::variant<Args...>& f)
223 r.stream().stream << (int)f.index();
224 std::visit(*
this, f);
230 std::function<void(QByteArray)>& bus;
233 void operator()() { this->bus(QByteArray{}); }
235 template <
typename T>
236 requires std::is_trivial_v<T>
237 void operator()(
const T& msg)
240 this->bus(QByteArray((
const char*)&msg,
sizeof(msg)));
243 template <
typename T>
245 !std::is_trivial_v<T> && avnd::relocatable<T>
246 &&
alignof(T) <=
alignof(max_align_t))
247 void operator()(
const T& msg)
249 QByteArray b(
sizeof(msg), Qt::Uninitialized);
250 auto dst =
reinterpret_cast<T*
>(b.data());
253 this->bus(std::move(b));
256 template <
typename T>
258 !std::is_trivial_v<T> && avnd::relocatable<T>
259 &&
alignof(T) <=
alignof(max_align_t))
260 void operator()(T&& msg)
262 QByteArray b(
sizeof(msg), Qt::Uninitialized);
263 auto dst =
reinterpret_cast<T*
>(b.data());
264 std::construct_at(dst, std::move(msg));
266 this->bus(std::move(b));
269 template <
typename T>
271 !std::is_trivial_v<T>
272 && (!avnd::relocatable<T> || (
alignof(T) >
alignof(max_align_t))))
273 void operator()(
const T& msg)
281 this->bus(std::move(buf));
284 template <
typename T>
285 void operator()(
const std::shared_ptr<T>& msg)
288 return (*
this)(std::move(*msg));
290 template <
typename T>
291 void operator()(std::unique_ptr<T> msg)
294 return (*
this)(std::move(*msg));
302 template <
typename F>
303 requires std::is_aggregate_v<F>
304 void operator()(F& f)
306 avnd::pfr::for_each_field(f, *
this);
309 template <
typename F>
310 requires(std::is_arithmetic_v<F>)
311 void operator()(F& f)
313 r.stream().stream >> f;
316 template <
typename F>
317 requires avnd::list_ish<F>
318 void operator()(F& f)
321 r.stream().stream >> sz;
322 SCORE_ASSERT(sz >= 0);
323 for(int64_t i = 0; i < sz; i++)
325 using type =
typename F::value_type;
328 f.push_back(std::move(val));
332 template <std::size_t I,
typename... Args>
333 bool write_variant(std::variant<Args...>& f)
335 auto& elt = f.template emplace<I>();
340 template <
typename... Args>
341 void operator()(std::variant<Args...>& f)
344 r.stream().stream >> index;
346 SCORE_ASSERT(index >= 0);
347 SCORE_ASSERT(index <
sizeof...(Args));
348 [&]<std::size_t... I>(std::index_sequence<I...>)
350 (((index == I) && write_variant<I>(f)) || ...);
352 (std::make_index_sequence<
sizeof...(Args)>{});
355 void operator()(
auto& f) { r.stream() >> f; }
363 void operator()() { }
365 template <
typename T>
366 requires std::is_trivial_v<T>
367 void operator()(T& msg)
370 memcpy(&msg, mess.data(), mess.size());
372 template <
typename T>
374 !std::is_trivial_v<T> && avnd::relocatable<T>
375 &&
alignof(T) <=
alignof(max_align_t))
376 void operator()(T& msg)
378 auto src =
reinterpret_cast<T*
>(mess.data());
379 msg = std::move(*src);
380 std::destroy_at(src);
383 template <
typename T>
385 !std::is_trivial_v<T>
386 && (!avnd::relocatable<T> || (
alignof(T) >
alignof(max_align_t))))
387 void operator()(T& msg)
401template <
typename... T>
404 if(
auto q = gcq.lock())
405 q->enqueue(Execution::gc(std::move(t)...));
418template <
typename Node>
420 Node& eff, std::weak_ptr<Execution::ExecutionCommandQueue> qex_ptr,
421 std::weak_ptr<Execution::GCCommandQueue> gcq)
423 return [qex_ptr = std::move(qex_ptr), gcq = std::move(gcq), &eff](QByteArray b) {
424 auto qex = qex_ptr.lock();
428 using refl = avnd::function_reflection<&Node::process_message>;
429 static_assert(refl::count <= 1);
431 if constexpr(refl::count == 0)
433 qex->enqueue([&eff] { eff.process_message(); });
437 using arg_type = avnd::first_argument<&Node::process_message>;
438 using message_type = std::decay_t<arg_type>;
439 static constexpr auto deliver = [](Node& eff, message_type& m) {
440 if constexpr(std::is_lvalue_reference_v<arg_type>)
441 eff.process_message(m);
443 eff.process_message(std::move(m));
447 std::is_trivially_destructible_v<message_type>
448 &&
sizeof(message_type) +
sizeof(Node*)
449 < Execution::ExecutionCommand::max_storage)
453 qex->enqueue([msg, &eff]()
mutable { deliver(eff, msg); });
457 auto msg = std::make_unique<message_type>();
459 qex->enqueue([msg = std::move(msg), gcq, &eff]()
mutable {
Definition DataStreamVisitor.hpp:27
Definition DataStreamVisitor.hpp:202
Definition Controls.hpp:30
std::function< void(QByteArray)> ui_to_processor_bus(Node &eff, std::weak_ptr< Execution::ExecutionCommandQueue > qex_ptr, std::weak_ptr< Execution::GCCommandQueue > gcq)
Definition MessageBus.hpp:419
void free_on_main_thread(const std::weak_ptr< Execution::GCCommandQueue > &gcq, T &... t)
Definition MessageBus.hpp:402
Definition MessageBus.hpp:299
Definition MessageBus.hpp:359
Definition MessageBus.hpp:229
Definition MessageBus.hpp:198
Definition MessageBus.hpp:44
void park() noexcept
Writer's thread: the wake-up could not be sent.
Definition MessageBus.hpp:50
bool unpark() noexcept
Writer's thread: whether a wake-up was parked since the last call.
Definition MessageBus.hpp:56
The argument type of a std::function<void(Arg)>.
Definition MessageBus.hpp:26
Definition MessageBus.hpp:162
T * take() noexcept
Definition MessageBus.hpp:179
Definition MessageBus.hpp:106
int drain(F &&f)
UI thread: calls f with every message written since the last call.
Definition MessageBus.hpp:124
Definition MessageBus.hpp:73
void add(std::function< bool()> f)
UI thread, before the node runs. f returns false when it is still parked.
Definition MessageBus.hpp:75
void retry() noexcept
Writer's thread, at each tick.
Definition MessageBus.hpp:81
void park() noexcept
Writer's thread: a channel parked its wake-up.
Definition MessageBus.hpp:78