10 }
catch (
const std::exception& e) {
12 "Exception in Routine on_complete: {}", e.what());
15 "Unknown exception in Routine on_complete");
21 return { std::coroutine_handle<audio_promise>::from_promise(*
this) };
26 return { std::coroutine_handle<graphics_promise>::from_promise(*
this) };
31 return { std::coroutine_handle<cross_promise>::from_promise(*
this) };
36 return FreeRoutine { std::coroutine_handle<conditional_promise>::from_promise(*
this) };
44 "SoundRoutine constructed with invalid coroutine handle");
49 : m_handle(std::exchange(other.m_handle, {}))
56 if (m_handle && m_handle.address())
59 m_handle = std::exchange(other.m_handle, {});
75 return m_handle.promise().processing_token;
92 m_handle.promise().next_sample = current_sample;
93 m_handle.promise().next_buffer_cycle = current_sample;
108 return m_handle.promise().sync_to_clock;
116 auto& promise_ref =
m_handle.promise();
118 if (promise_ref.should_terminate || !promise_ref.auto_resume) {
126 if (promise_ref.active_delay_context !=
DelayContext::NONE && promise_ref.active_delay_context != context) {
130 bool should_resume =
false;
135 should_resume = (current_value >= promise_ref.next_sample);
137 promise_ref.next_sample = current_value + promise_ref.delay_amount;
140 should_resume =
false;
146 should_resume = (current_value >= promise_ref.next_buffer_cycle);
148 should_resume =
false;
153 should_resume =
true;
187 set_state<bool>(
"restart",
true);
188 m_handle.promise().auto_resume =
true;
209 auto& state_map =
m_handle.promise().state;
210 auto it = state_map.find(key);
211 if (it != state_map.end()) {
222 "GraphicsRoutine constructed with invalid coroutine handle");
227 : m_handle(std::exchange(other.m_handle,
nullptr))
233 if (
this != &other) {
234 m_handle = std::exchange(other.m_handle,
nullptr);
262 m_handle.promise().next_frame = current_frame;
277 return m_handle.promise().sync_to_clock;
285 auto& promise_ref =
m_handle.promise();
287 if (promise_ref.should_terminate || !promise_ref.auto_resume) {
295 if (promise_ref.active_delay_context !=
DelayContext::NONE && promise_ref.active_delay_context != context) {
299 bool should_resume =
false;
304 should_resume = (current_value >= promise_ref.next_frame);
306 promise_ref.next_frame = current_value + promise_ref.delay_amount;
309 should_resume =
false;
314 should_resume =
true;
348 set_state<bool>(
"restart",
true);
349 m_handle.promise().auto_resume =
true;
370 auto& state_map =
m_handle.promise().state;
371 auto it = state_map.find(key);
372 if (it != state_map.end()) {
383 std::source_location::current(),
384 "CrossRoutine constructed with invalid coroutine handle");
389 : m_handle(other.m_handle)
398 if (
this != &other) {
413 : m_handle(std::exchange(other.m_handle, {}))
419 if (
this != &other) {
420 if (m_handle && m_handle.address())
422 m_handle = std::exchange(other.m_handle, {});
438 return m_handle.promise().processing_token;
455 m_handle.promise().next_sample.store(current_context, std::memory_order_release);
456 m_handle.promise().next_frame.store(current_context, std::memory_order_release);
463 MF_PRINT(
"CrossRoutine next_execution called. is_active: {}, next_sample: {}, next_frame: {}",
465 is_active() ?
m_handle.promise().next_sample.load(std::memory_order_acquire) : UINT64_MAX,
466 is_active() ?
m_handle.promise().next_frame.load(std::memory_order_acquire) : UINT64_MAX);
469 ?
m_handle.promise().next_sample.load(std::memory_order_acquire)
478 return m_handle.promise().sync_to_clock;
486 auto& promise_ref =
m_handle.promise();
488 if (promise_ref.should_terminate || !promise_ref.auto_resume) {
492 const DelayContext active = promise_ref.active_delay_context.load(std::memory_order_acquire);
503 if (promise_ref.sample_delay_amount > 0
504 && current_value >= promise_ref.next_sample.load(std::memory_order_acquire)) {
505 promise_ref.sample_satisfied.store(
true, std::memory_order_release);
508 if (promise_ref.frame_delay_amount > 0
509 && current_value >= promise_ref.next_frame.load(std::memory_order_acquire)) {
510 promise_ref.frame_satisfied.store(
true, std::memory_order_release);
514 bool sample_required = promise_ref.sample_delay_amount > 0;
515 bool frame_required = promise_ref.frame_delay_amount > 0;
517 if (sample_required && !promise_ref.sample_satisfied.load(std::memory_order_acquire)) {
520 if (frame_required && !promise_ref.frame_satisfied.load(std::memory_order_acquire)) {
525 if (!promise_ref.active_delay_context.compare_exchange_strong(
527 std::memory_order_acq_rel, std::memory_order_acquire)) {
531 promise_ref.sample_satisfied.store(
false, std::memory_order_release);
532 promise_ref.frame_satisfied.store(
false, std::memory_order_release);
557 set_state<bool>(
"restart",
true);
558 m_handle.promise().auto_resume =
true;
579 auto& state_map =
m_handle.promise().state;
580 auto it = state_map.find(key);
581 if (it != state_map.end()) {
591 error<std::invalid_argument>(
593 std::source_location::current(),
594 "FreeRoutine: invalid coroutine handle");
599 : m_handle(std::exchange(other.m_handle, {}))
605 if (
this != &other) {
606 if (m_handle && m_handle.address())
608 m_handle = std::exchange(other.m_handle, {});
634 m_handle.promise().auto_resume =
true;
650 if (p.should_terminate || !p.auto_resume)
653 if (!p.armed.load(std::memory_order_acquire))
656 if (!p.condition || !p.condition())
659 p.armed.store(
false, std::memory_order_release);
660 p.condition =
nullptr;
677 set_state<bool>(
"restart",
true);
678 m_handle.promise().auto_resume =
true;
697 auto it = s.find(key);
698 return it != s.end() ? &it->second :
nullptr;
#define MF_RT_ERROR(comp, ctx,...)
std::coroutine_handle< promise_type > m_handle
bool initialize_state(uint64_t current_context=0U) override
Initializes the coroutine's state for execution.
bool try_resume_with_context(uint64_t current_value, DelayContext context) override
Attempts to resume the coroutine with explicit temporal context.
bool try_resume(uint64_t current_context) override
Attempts to resume the coroutine if it's ready to execute.
uint64_t next_execution() const override
Gets the sample position when this routine should next execute.
void * get_state_impl_raw(const std::string &key) override
CrossRoutine & operator=(const CrossRoutine &other)
bool restart() override
Restarts the coroutine from the beginning.
ProcessingToken get_processing_token() const override
Get the processing token that determines how this routine should be scheduled.
void set_state_impl(const std::string &key, std::any value) override
bool requires_clock_sync() const override
Check if the routine should synchronize with a clock.
bool is_active() const override
Checks if the coroutine is still active.
bool force_resume() override
Force resume the coroutine, bypassing all checks Used only during shutdown to push coroutines to fina...
CrossRoutine(std::coroutine_handle< promise_type > h)
Coroutine resumed by more than one clock.
bool initialize_state(uint64_t current_context=0U) override
Initializes the coroutine's state for execution.
ProcessingToken get_processing_token() const override
Get the processing token that determines how this routine should be scheduled.
std::coroutine_handle< promise_type > m_handle
bool try_resume(uint64_t current_context) override
Attempts to resume the coroutine if it's ready to execute.
FreeRoutine & operator=(const FreeRoutine &other)=delete
FreeRoutine(std::coroutine_handle< promise_type > h)
void * get_state_impl_raw(const std::string &key) override
bool force_resume() override
Force resume the coroutine, bypassing all checks Used only during shutdown to push coroutines to fina...
bool is_active() const override
Checks if the coroutine is still active.
void set_state_impl(const std::string &key, std::any value) override
bool restart() override
Restarts the coroutine from the beginning.
bool try_resume_with_context(uint64_t current_value, DelayContext context) override
Attempts to resume the coroutine with explicit temporal context.
Coroutine resumed when a caller-supplied condition becomes true.
void * get_state_impl_raw(const std::string &key) override
bool force_resume() override
Force resume the coroutine, bypassing all checks Used only during shutdown to push coroutines to fina...
bool initialize_state(uint64_t current_frame=0U) override
Initializes the coroutine's state for execution.
void set_state_impl(const std::string &key, std::any value) override
bool try_resume_with_context(uint64_t current_value, DelayContext context) override
Attempts to resume the coroutine with explicit temporal context.
bool is_active() const override
Checks if the coroutine is still active.
~GraphicsRoutine() override
bool requires_clock_sync() const override
Check if the routine should synchronize with a clock.
bool restart() override
Restarts the coroutine from the beginning.
GraphicsRoutine & operator=(const GraphicsRoutine &other)=delete
uint64_t next_execution() const override
Gets the sample position when this routine should next execute.
std::coroutine_handle< promise_type > m_handle
Handle to the underlying coroutine.
bool try_resume(uint64_t current_context) override
Attempts to resume the coroutine if it's ready to execute.
ProcessingToken get_processing_token() const override
Get the processing token that determines how this routine should be scheduled.
GraphicsRoutine(std::coroutine_handle< promise_type > h)
Constructs a GraphicsRoutine from a coroutine handle.
A C++20 coroutine-based graphics processing task with frame-accurate timing.
std::coroutine_handle< promise_type > m_handle
Handle to the underlying coroutine.
bool is_active() const override
Checks if the coroutine is still active.
bool try_resume_with_context(uint64_t current_value, DelayContext context) override
Attempts to resume the coroutine with explicit temporal context.
void * get_state_impl_raw(const std::string &key) override
SoundRoutine & operator=(const SoundRoutine &other)=delete
uint64_t next_execution() const override
Gets the sample position when this routine should next execute.
void set_state_impl(const std::string &key, std::any value) override
bool requires_clock_sync() const override
Check if the routine should synchronize with a clock.
bool try_resume(uint64_t current_context) override
Attempts to resume the coroutine if it's ready to execute.
bool force_resume() override
Force resume the coroutine, bypassing all checks Used only during shutdown to push coroutines to fina...
SoundRoutine(std::coroutine_handle< promise_type > h)
Constructs a SoundRoutine from a coroutine handle.
bool restart() override
Restarts the coroutine from the beginning.
ProcessingToken get_processing_token() const override
Get the processing token that determines how this routine should be scheduled.
bool initialize_state(uint64_t current_sample=0U) override
Initializes the coroutine's state for execution.
A C++20 coroutine-based audio processing task with sample-accurate timing.
@ CoroutineScheduling
Coroutine scheduling and temporal coordination (Vruta::TaskScheduler)
@ Vruta
Coroutines, schedulers, clocks, task management.
@ CONDITIONAL
Condition-driven execution - resume when a caller-supplied predicate returns true.
@ FRAME_ACCURATE
Coroutine is frame-accurate.
@ ON_DEMAND
Coroutine is executed on demand, not scheduled.
MAYAFLUX_API void invoke_routine_completion(std::function< void()> callback) noexcept
DelayContext
Discriminator for different temporal delay mechanisms.
@ MULTIPLE
Armed on both sample and frame clocks; first to arrive resumes (CrossRoutine)
@ FRAME_BASED
Frame-rate delay (Graphics domain)
@ NONE
No active delay, try resume immediately.
@ SAMPLE_BASED
Sample-accurate delay (audio domain)
@ BUFFER_BASED
Buffer-cycle delay (audio hardware boundary)
@ AWAIT
Awaiter-induced delay (temporary suspension)
SoundRoutine get_return_object()
Creates the SoundRoutine object returned to the caller.
FreeRoutine get_return_object()
CrossRoutine get_return_object()
GraphicsRoutine get_return_object()
Creates the GraphicsRoutine object returned to the caller.