8 return { std::coroutine_handle<audio_promise>::from_promise(*
this) };
13 return { std::coroutine_handle<graphics_promise>::from_promise(*
this) };
18 return { std::coroutine_handle<cross_promise>::from_promise(*
this) };
23 return FreeRoutine { std::coroutine_handle<conditional_promise>::from_promise(*
this) };
31 "SoundRoutine constructed with invalid coroutine handle");
36 : m_handle(std::exchange(other.m_handle, {}))
43 if (m_handle && m_handle.address())
46 m_handle = std::exchange(other.m_handle, {});
62 return m_handle.promise().processing_token;
79 m_handle.promise().next_sample = current_sample;
80 m_handle.promise().next_buffer_cycle = current_sample;
95 return m_handle.promise().sync_to_clock;
103 auto& promise_ref =
m_handle.promise();
105 if (promise_ref.should_terminate || !promise_ref.auto_resume) {
113 if (promise_ref.active_delay_context !=
DelayContext::NONE && promise_ref.active_delay_context != context) {
117 bool should_resume =
false;
122 should_resume = (current_value >= promise_ref.next_sample);
124 promise_ref.next_sample = current_value + promise_ref.delay_amount;
127 should_resume =
false;
133 should_resume = (current_value >= promise_ref.next_buffer_cycle);
135 should_resume =
false;
140 should_resume =
true;
174 set_state<bool>(
"restart",
true);
175 m_handle.promise().auto_resume =
true;
186 m_handle.promise().state[key] = std::move(value);
196 auto& state_map =
m_handle.promise().state;
197 auto it = state_map.find(key);
198 if (it != state_map.end()) {
209 "GraphicsRoutine constructed with invalid coroutine handle");
214 : m_handle(std::exchange(other.m_handle,
nullptr))
220 if (
this != &other) {
221 m_handle = std::exchange(other.m_handle,
nullptr);
249 m_handle.promise().next_frame = current_frame;
264 return m_handle.promise().sync_to_clock;
272 auto& promise_ref =
m_handle.promise();
274 if (promise_ref.should_terminate || !promise_ref.auto_resume) {
282 if (promise_ref.active_delay_context !=
DelayContext::NONE && promise_ref.active_delay_context != context) {
286 bool should_resume =
false;
291 should_resume = (current_value >= promise_ref.next_frame);
293 promise_ref.next_frame = current_value + promise_ref.delay_amount;
296 should_resume =
false;
301 should_resume =
true;
335 set_state<bool>(
"restart",
true);
336 m_handle.promise().auto_resume =
true;
347 m_handle.promise().state[key] = std::move(value);
357 auto& state_map =
m_handle.promise().state;
358 auto it = state_map.find(key);
359 if (it != state_map.end()) {
370 std::source_location::current(),
371 "CrossRoutine constructed with invalid coroutine handle");
376 : m_handle(other.m_handle)
385 if (
this != &other) {
400 : m_handle(std::exchange(other.m_handle, {}))
406 if (
this != &other) {
407 if (m_handle && m_handle.address())
409 m_handle = std::exchange(other.m_handle, {});
425 return m_handle.promise().processing_token;
442 m_handle.promise().next_sample.store(current_context, std::memory_order_release);
443 m_handle.promise().next_frame.store(current_context, std::memory_order_release);
450 MF_PRINT(
"CrossRoutine next_execution called. is_active: {}, next_sample: {}, next_frame: {}",
452 is_active() ?
m_handle.promise().next_sample.load(std::memory_order_acquire) : UINT64_MAX,
453 is_active() ?
m_handle.promise().next_frame.load(std::memory_order_acquire) : UINT64_MAX);
456 ?
m_handle.promise().next_sample.load(std::memory_order_acquire)
465 return m_handle.promise().sync_to_clock;
473 auto& promise_ref =
m_handle.promise();
475 if (promise_ref.should_terminate || !promise_ref.auto_resume) {
479 const DelayContext active = promise_ref.active_delay_context.load(std::memory_order_acquire);
490 if (promise_ref.sample_delay_amount > 0
491 && current_value >= promise_ref.next_sample.load(std::memory_order_acquire)) {
492 promise_ref.sample_satisfied.store(
true, std::memory_order_release);
495 if (promise_ref.frame_delay_amount > 0
496 && current_value >= promise_ref.next_frame.load(std::memory_order_acquire)) {
497 promise_ref.frame_satisfied.store(
true, std::memory_order_release);
501 bool sample_required = promise_ref.sample_delay_amount > 0;
502 bool frame_required = promise_ref.frame_delay_amount > 0;
504 if (sample_required && !promise_ref.sample_satisfied.load(std::memory_order_acquire)) {
507 if (frame_required && !promise_ref.frame_satisfied.load(std::memory_order_acquire)) {
512 if (!promise_ref.active_delay_context.compare_exchange_strong(
514 std::memory_order_acq_rel, std::memory_order_acquire)) {
518 promise_ref.sample_satisfied.store(
false, std::memory_order_release);
519 promise_ref.frame_satisfied.store(
false, std::memory_order_release);
544 set_state<bool>(
"restart",
true);
545 m_handle.promise().auto_resume =
true;
557 m_handle.promise().state[key] = std::move(value);
566 auto& state_map =
m_handle.promise().state;
567 auto it = state_map.find(key);
568 if (it != state_map.end()) {
578 error<std::invalid_argument>(
580 std::source_location::current(),
581 "FreeRoutine: invalid coroutine handle");
586 : m_handle(std::exchange(other.m_handle, {}))
592 if (
this != &other) {
593 if (m_handle && m_handle.address())
595 m_handle = std::exchange(other.m_handle, {});
621 m_handle.promise().auto_resume =
true;
637 if (p.should_terminate || !p.auto_resume)
640 if (!p.armed.load(std::memory_order_acquire))
643 if (!p.condition || !p.condition())
646 p.armed.store(
false, std::memory_order_release);
647 p.condition =
nullptr;
664 set_state<bool>(
"restart",
true);
665 m_handle.promise().auto_resume =
true;
676 m_handle.promise().state[key] = std::move(value);
684 auto it = s.find(key);
685 return it != s.end() ? &it->second :
nullptr;
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.
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.