MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Routine.cpp
Go to the documentation of this file.
1#include "Routine.hpp"
3
4namespace MayaFlux::Vruta {
5
7{
8 return { std::coroutine_handle<audio_promise>::from_promise(*this) };
9}
10
12{
13 return { std::coroutine_handle<graphics_promise>::from_promise(*this) };
14}
15
17{
18 return { std::coroutine_handle<cross_promise>::from_promise(*this) };
19}
20
22{
23 return FreeRoutine { std::coroutine_handle<conditional_promise>::from_promise(*this) };
24}
25
26SoundRoutine::SoundRoutine(std::coroutine_handle<promise_type> h)
27 : m_handle(h)
28{
29 if (!m_handle || !m_handle.address()) {
30 error<std::invalid_argument>(Journal::Component::Vruta, Journal::Context::CoroutineScheduling, std::source_location::current(),
31 "SoundRoutine constructed with invalid coroutine handle");
32 }
33}
34
36 : m_handle(std::exchange(other.m_handle, {}))
37{
38}
39
41{
42 if (this != &other) {
43 if (m_handle && m_handle.address())
44 m_handle.destroy();
45
46 m_handle = std::exchange(other.m_handle, {});
47 }
48 return *this;
49}
50
52{
53 if (m_handle && m_handle.address())
54 m_handle.destroy();
55}
56
58{
59 if (!m_handle) {
61 }
62 return m_handle.promise().processing_token;
63}
64
66{
67 if (!m_handle) {
68 return false;
69 }
70 return m_handle.address() != nullptr && !m_handle.done();
71}
72
73bool SoundRoutine::initialize_state(uint64_t current_sample)
74{
75 if (!m_handle || !m_handle.address() || m_handle.done()) {
76 return false;
77 }
78
79 m_handle.promise().next_sample = current_sample;
80 m_handle.promise().next_buffer_cycle = current_sample;
81 m_handle.resume();
82 return true;
83}
84
86{
87 return is_active() ? m_handle.promise().next_sample : UINT64_MAX;
88}
89
91{
92 if (!m_handle) {
93 return false;
94 }
95 return m_handle.promise().sync_to_clock;
96}
97
98bool SoundRoutine::try_resume_with_context(uint64_t current_value, DelayContext context)
99{
100 if (!is_active())
101 return false;
102
103 auto& promise_ref = m_handle.promise();
104
105 if (promise_ref.should_terminate || !promise_ref.auto_resume) {
106 return false;
107 }
108
109 if (context != DelayContext::NONE && promise_ref.active_delay_context == DelayContext::AWAIT) {
110 return initialize_state(current_value);
111 }
112
113 if (promise_ref.active_delay_context != DelayContext::NONE && promise_ref.active_delay_context != context) {
114 return false;
115 }
116
117 bool should_resume = false;
118
119 switch (context) {
121 if (promise_ref.active_delay_context == DelayContext::SAMPLE_BASED) {
122 should_resume = (current_value >= promise_ref.next_sample);
123 if (should_resume) {
124 promise_ref.next_sample = current_value + promise_ref.delay_amount;
125 }
126 } else {
127 should_resume = false;
128 }
129 break;
130
132 if (promise_ref.active_delay_context == DelayContext::BUFFER_BASED) {
133 should_resume = (current_value >= promise_ref.next_buffer_cycle);
134 } else {
135 should_resume = false;
136 }
137 break;
138
140 should_resume = true;
141 break;
142
143 default:
144 return false;
145 }
146
147 if (should_resume) {
148 m_handle.resume();
149 return true;
150 }
151
152 return false;
153}
154
155bool SoundRoutine::try_resume(uint64_t current_context)
156{
158}
159
161{
162 if (!m_handle || m_handle.done()) {
163 return false;
164 }
165 m_handle.resume();
166 return true;
167}
168
170{
171 if (!m_handle)
172 return false;
173
174 set_state<bool>("restart", true);
175 m_handle.promise().auto_resume = true;
176 if (is_active()) {
177 m_handle.resume();
178 return true;
179 }
180 return false;
181}
182
183void SoundRoutine::set_state_impl(const std::string& key, std::any value)
184{
185 if (m_handle) {
186 m_handle.promise().state[key] = std::move(value);
187 }
188}
189
190void* SoundRoutine::get_state_impl_raw(const std::string& key)
191{
192 if (!m_handle) {
193 return nullptr;
194 }
195
196 auto& state_map = m_handle.promise().state;
197 auto it = state_map.find(key);
198 if (it != state_map.end()) {
199 return &it->second;
200 }
201 return nullptr;
202}
203
204GraphicsRoutine::GraphicsRoutine(std::coroutine_handle<promise_type> h)
205 : m_handle(h)
206{
207 if (!m_handle || !m_handle.address()) {
208 error<std::invalid_argument>(Journal::Component::Vruta, Journal::Context::CoroutineScheduling, std::source_location::current(),
209 "GraphicsRoutine constructed with invalid coroutine handle");
210 }
211}
212
214 : m_handle(std::exchange(other.m_handle, nullptr))
215{
216}
217
219{
220 if (this != &other) {
221 m_handle = std::exchange(other.m_handle, nullptr);
222 }
223 return *this;
224}
225
227{
228 if (m_handle) {
229 m_handle.destroy();
230 }
231}
232
237
239{
240 return m_handle && !m_handle.done();
241}
242
243bool GraphicsRoutine::initialize_state(uint64_t current_frame)
244{
245 if (!is_active() || !m_handle.address()) {
246 return false;
247 }
248
249 m_handle.promise().next_frame = current_frame;
250 m_handle.resume();
251 return true;
252}
253
255{
256 return m_handle ? m_handle.promise().next_frame : UINT64_MAX;
257}
258
260{
261 if (!m_handle) {
262 return false;
263 }
264 return m_handle.promise().sync_to_clock;
265}
266
267bool GraphicsRoutine::try_resume_with_context(uint64_t current_value, DelayContext context)
268{
269 if (!is_active())
270 return false;
271
272 auto& promise_ref = m_handle.promise();
273
274 if (promise_ref.should_terminate || !promise_ref.auto_resume) {
275 return false;
276 }
277
278 if (context != DelayContext::NONE && promise_ref.active_delay_context == DelayContext::AWAIT) {
279 return initialize_state(current_value);
280 }
281
282 if (promise_ref.active_delay_context != DelayContext::NONE && promise_ref.active_delay_context != context) {
283 return false;
284 }
285
286 bool should_resume = false;
287
288 switch (context) {
290 if (promise_ref.active_delay_context == DelayContext::FRAME_BASED) {
291 should_resume = (current_value >= promise_ref.next_frame);
292 if (should_resume) {
293 promise_ref.next_frame = current_value + promise_ref.delay_amount;
294 }
295 } else {
296 should_resume = false;
297 }
298 break;
299
301 should_resume = true;
302 break;
303
304 default:
305 return false;
306 }
307
308 if (should_resume) {
309 m_handle.resume();
310 return true;
311 }
312
313 return false;
314}
315
316bool GraphicsRoutine::try_resume(uint64_t current_context)
317{
318 return try_resume_with_context(current_context, DelayContext::FRAME_BASED);
319}
320
322{
323 if (!m_handle || m_handle.done()) {
324 return false;
325 }
326 m_handle.resume();
327 return true;
328}
329
331{
332 if (!m_handle)
333 return false;
334
335 set_state<bool>("restart", true);
336 m_handle.promise().auto_resume = true;
337 if (is_active()) {
338 m_handle.resume();
339 return true;
340 }
341 return false;
342}
343
344void GraphicsRoutine::set_state_impl(const std::string& key, std::any value)
345{
346 if (m_handle) {
347 m_handle.promise().state[key] = std::move(value);
348 }
349}
350
351void* GraphicsRoutine::get_state_impl_raw(const std::string& key)
352{
353 if (!m_handle) {
354 return nullptr;
355 }
356
357 auto& state_map = m_handle.promise().state;
358 auto it = state_map.find(key);
359 if (it != state_map.end()) {
360 return &it->second;
361 }
362 return nullptr;
363}
364
365CrossRoutine::CrossRoutine(std::coroutine_handle<promise_type> h)
366 : m_handle(h)
367{
368 if (!m_handle || !m_handle.address()) {
370 std::source_location::current(),
371 "CrossRoutine constructed with invalid coroutine handle");
372 }
373}
374
376 : m_handle(other.m_handle)
377{
378 if (other.m_handle) {
379 m_handle = other.m_handle;
380 }
381}
382
384{
385 if (this != &other) {
386 if (m_handle) {
387 m_handle.destroy();
388 }
389
390 if (other.m_handle) {
391 m_handle = other.m_handle;
392 } else {
393 m_handle = nullptr;
394 }
395 }
396 return *this;
397}
398
400 : m_handle(std::exchange(other.m_handle, {}))
401{
402}
403
405{
406 if (this != &other) {
407 if (m_handle && m_handle.address())
408 m_handle.destroy();
409 m_handle = std::exchange(other.m_handle, {});
410 }
411 return *this;
412}
413
415{
416 if (m_handle && m_handle.address())
417 m_handle.destroy();
418}
419
421{
422 if (!m_handle) {
424 }
425 return m_handle.promise().processing_token;
426}
427
429{
430 if (!m_handle) {
431 return false;
432 }
433 return m_handle.address() != nullptr && !m_handle.done();
434}
435
436bool CrossRoutine::initialize_state(uint64_t current_context)
437{
438 if (!m_handle || !m_handle.address() || m_handle.done()) {
439 return false;
440 }
441
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);
444 m_handle.resume();
445 return true;
446}
447
449{
450 MF_PRINT("CrossRoutine next_execution called. is_active: {}, next_sample: {}, next_frame: {}",
451 is_active(),
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);
454
455 return is_active()
456 ? m_handle.promise().next_sample.load(std::memory_order_acquire)
457 : UINT64_MAX;
458}
459
461{
462 if (!m_handle) {
463 return false;
464 }
465 return m_handle.promise().sync_to_clock;
466}
467
468bool CrossRoutine::try_resume_with_context(uint64_t current_value, DelayContext context)
469{
470 if (!is_active())
471 return false;
472
473 auto& promise_ref = m_handle.promise();
474
475 if (promise_ref.should_terminate || !promise_ref.auto_resume) {
476 return false;
477 }
478
479 const DelayContext active = promise_ref.active_delay_context.load(std::memory_order_acquire);
480
481 if (context != DelayContext::NONE && active == DelayContext::AWAIT) {
482 return initialize_state(current_value);
483 }
484
485 if (active != DelayContext::MULTIPLE) {
486 return false;
487 }
488
489 if (context == DelayContext::SAMPLE_BASED) {
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);
493 }
494 } else if (context == DelayContext::FRAME_BASED) {
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);
498 }
499 }
500
501 bool sample_required = promise_ref.sample_delay_amount > 0;
502 bool frame_required = promise_ref.frame_delay_amount > 0;
503
504 if (sample_required && !promise_ref.sample_satisfied.load(std::memory_order_acquire)) {
505 return false;
506 }
507 if (frame_required && !promise_ref.frame_satisfied.load(std::memory_order_acquire)) {
508 return false;
509 }
510
512 if (!promise_ref.active_delay_context.compare_exchange_strong(
513 expected, DelayContext::NONE,
514 std::memory_order_acq_rel, std::memory_order_acquire)) {
515 return false;
516 }
517
518 promise_ref.sample_satisfied.store(false, std::memory_order_release);
519 promise_ref.frame_satisfied.store(false, std::memory_order_release);
520
521 m_handle.resume();
522 return true;
523}
524
525bool CrossRoutine::try_resume(uint64_t current_context)
526{
528}
529
531{
532 if (!m_handle || m_handle.done()) {
533 return false;
534 }
535 m_handle.resume();
536 return true;
537}
538
540{
541 if (!m_handle)
542 return false;
543
544 set_state<bool>("restart", true);
545 m_handle.promise().auto_resume = true;
546 if (is_active()) {
547 m_handle.resume();
548 return true;
549 }
550
551 return false;
552}
553
554void CrossRoutine::set_state_impl(const std::string& key, std::any value)
555{
556 if (m_handle) {
557 m_handle.promise().state[key] = std::move(value);
558 }
559}
560
561void* CrossRoutine::get_state_impl_raw(const std::string& key)
562{
563 if (!m_handle) {
564 return nullptr;
565 }
566 auto& state_map = m_handle.promise().state;
567 auto it = state_map.find(key);
568 if (it != state_map.end()) {
569 return &it->second;
570 }
571 return nullptr;
572}
573
574FreeRoutine::FreeRoutine(std::coroutine_handle<promise_type> h)
575 : m_handle(h)
576{
577 if (!m_handle || !m_handle.address()) {
578 error<std::invalid_argument>(
580 std::source_location::current(),
581 "FreeRoutine: invalid coroutine handle");
582 }
583}
584
586 : m_handle(std::exchange(other.m_handle, {}))
587{
588}
589
591{
592 if (this != &other) {
593 if (m_handle && m_handle.address())
594 m_handle.destroy();
595 m_handle = std::exchange(other.m_handle, {});
596 }
597 return *this;
598}
599
601{
602 if (m_handle && m_handle.address())
603 m_handle.destroy();
604}
605
610
612{
613 return m_handle && m_handle.address() && !m_handle.done();
614}
615
616bool FreeRoutine::initialize_state(uint64_t /*current_context*/)
617{
618 if (!m_handle || m_handle.done())
619 return false;
620
621 m_handle.promise().auto_resume = true;
622 return true;
623}
624
625bool FreeRoutine::try_resume(uint64_t /*current_context*/)
626{
628}
629
630bool FreeRoutine::try_resume_with_context(uint64_t /*current_value*/, DelayContext /*context*/)
631{
632 if (!is_active())
633 return false;
634
635 auto& p = m_handle.promise();
636
637 if (p.should_terminate || !p.auto_resume)
638 return false;
639
640 if (!p.armed.load(std::memory_order_acquire))
641 return false;
642
643 if (!p.condition || !p.condition())
644 return false;
645
646 p.armed.store(false, std::memory_order_release);
647 p.condition = nullptr;
648 m_handle.resume();
649 return true;
650}
651
653{
654 if (!m_handle || m_handle.done())
655 return false;
656 m_handle.resume();
657 return true;
658}
659
661{
662 if (!m_handle)
663 return false;
664 set_state<bool>("restart", true);
665 m_handle.promise().auto_resume = true;
666 if (is_active()) {
667 m_handle.resume();
668 return true;
669 }
670 return false;
671}
672
673void FreeRoutine::set_state_impl(const std::string& key, std::any value)
674{
675 if (m_handle)
676 m_handle.promise().state[key] = std::move(value);
677}
678
679void* FreeRoutine::get_state_impl_raw(const std::string& key)
680{
681 if (!m_handle)
682 return nullptr;
683 auto& s = m_handle.promise().state;
684 auto it = s.find(key);
685 return it != s.end() ? &it->second : nullptr;
686}
687
688}
#define MF_PRINT(...)
uint32_t h
Definition InkPress.cpp:28
std::coroutine_handle< promise_type > m_handle
Definition Routine.hpp:726
bool initialize_state(uint64_t current_context=0U) override
Initializes the coroutine's state for execution.
Definition Routine.cpp:436
bool try_resume_with_context(uint64_t current_value, DelayContext context) override
Attempts to resume the coroutine with explicit temporal context.
Definition Routine.cpp:468
bool try_resume(uint64_t current_context) override
Attempts to resume the coroutine if it's ready to execute.
Definition Routine.cpp:525
uint64_t next_execution() const override
Gets the sample position when this routine should next execute.
Definition Routine.cpp:448
void * get_state_impl_raw(const std::string &key) override
Definition Routine.cpp:561
CrossRoutine & operator=(const CrossRoutine &other)
Definition Routine.cpp:383
bool restart() override
Restarts the coroutine from the beginning.
Definition Routine.cpp:539
ProcessingToken get_processing_token() const override
Get the processing token that determines how this routine should be scheduled.
Definition Routine.cpp:420
void set_state_impl(const std::string &key, std::any value) override
Definition Routine.cpp:554
bool requires_clock_sync() const override
Check if the routine should synchronize with a clock.
Definition Routine.cpp:460
bool is_active() const override
Checks if the coroutine is still active.
Definition Routine.cpp:428
bool force_resume() override
Force resume the coroutine, bypassing all checks Used only during shutdown to push coroutines to fina...
Definition Routine.cpp:530
CrossRoutine(std::coroutine_handle< promise_type > h)
Definition Routine.cpp:365
Coroutine resumed by more than one clock.
Definition Routine.hpp:642
bool initialize_state(uint64_t current_context=0U) override
Initializes the coroutine's state for execution.
Definition Routine.cpp:616
ProcessingToken get_processing_token() const override
Get the processing token that determines how this routine should be scheduled.
Definition Routine.cpp:606
std::coroutine_handle< promise_type > m_handle
Definition Routine.hpp:811
bool try_resume(uint64_t current_context) override
Attempts to resume the coroutine if it's ready to execute.
Definition Routine.cpp:625
FreeRoutine & operator=(const FreeRoutine &other)=delete
FreeRoutine(std::coroutine_handle< promise_type > h)
Definition Routine.cpp:574
void * get_state_impl_raw(const std::string &key) override
Definition Routine.cpp:679
bool force_resume() override
Force resume the coroutine, bypassing all checks Used only during shutdown to push coroutines to fina...
Definition Routine.cpp:652
bool is_active() const override
Checks if the coroutine is still active.
Definition Routine.cpp:611
void set_state_impl(const std::string &key, std::any value) override
Definition Routine.cpp:673
bool restart() override
Restarts the coroutine from the beginning.
Definition Routine.cpp:660
bool try_resume_with_context(uint64_t current_value, DelayContext context) override
Attempts to resume the coroutine with explicit temporal context.
Definition Routine.cpp:630
Coroutine resumed when a caller-supplied condition becomes true.
Definition Routine.hpp:753
void * get_state_impl_raw(const std::string &key) override
Definition Routine.cpp:351
bool force_resume() override
Force resume the coroutine, bypassing all checks Used only during shutdown to push coroutines to fina...
Definition Routine.cpp:321
bool initialize_state(uint64_t current_frame=0U) override
Initializes the coroutine's state for execution.
Definition Routine.cpp:243
void set_state_impl(const std::string &key, std::any value) override
Definition Routine.cpp:344
bool try_resume_with_context(uint64_t current_value, DelayContext context) override
Attempts to resume the coroutine with explicit temporal context.
Definition Routine.cpp:267
bool is_active() const override
Checks if the coroutine is still active.
Definition Routine.cpp:238
bool requires_clock_sync() const override
Check if the routine should synchronize with a clock.
Definition Routine.cpp:259
bool restart() override
Restarts the coroutine from the beginning.
Definition Routine.cpp:330
GraphicsRoutine & operator=(const GraphicsRoutine &other)=delete
uint64_t next_execution() const override
Gets the sample position when this routine should next execute.
Definition Routine.cpp:254
std::coroutine_handle< promise_type > m_handle
Handle to the underlying coroutine.
Definition Routine.hpp:623
bool try_resume(uint64_t current_context) override
Attempts to resume the coroutine if it's ready to execute.
Definition Routine.cpp:316
ProcessingToken get_processing_token() const override
Get the processing token that determines how this routine should be scheduled.
Definition Routine.cpp:233
GraphicsRoutine(std::coroutine_handle< promise_type > h)
Constructs a GraphicsRoutine from a coroutine handle.
Definition Routine.cpp:204
A C++20 coroutine-based graphics processing task with frame-accurate timing.
Definition Routine.hpp:496
std::coroutine_handle< promise_type > m_handle
Handle to the underlying coroutine.
Definition Routine.hpp:452
bool is_active() const override
Checks if the coroutine is still active.
Definition Routine.cpp:65
bool try_resume_with_context(uint64_t current_value, DelayContext context) override
Attempts to resume the coroutine with explicit temporal context.
Definition Routine.cpp:98
void * get_state_impl_raw(const std::string &key) override
Definition Routine.cpp:190
SoundRoutine & operator=(const SoundRoutine &other)=delete
uint64_t next_execution() const override
Gets the sample position when this routine should next execute.
Definition Routine.cpp:85
void set_state_impl(const std::string &key, std::any value) override
Definition Routine.cpp:183
bool requires_clock_sync() const override
Check if the routine should synchronize with a clock.
Definition Routine.cpp:90
bool try_resume(uint64_t current_context) override
Attempts to resume the coroutine if it's ready to execute.
Definition Routine.cpp:155
bool force_resume() override
Force resume the coroutine, bypassing all checks Used only during shutdown to push coroutines to fina...
Definition Routine.cpp:160
SoundRoutine(std::coroutine_handle< promise_type > h)
Constructs a SoundRoutine from a coroutine handle.
Definition Routine.cpp:26
bool restart() override
Restarts the coroutine from the beginning.
Definition Routine.cpp:169
ProcessingToken get_processing_token() const override
Get the processing token that determines how this routine should be scheduled.
Definition Routine.cpp:57
bool initialize_state(uint64_t current_sample=0U) override
Initializes the coroutine's state for execution.
Definition Routine.cpp:73
A C++20 coroutine-based audio processing task with sample-accurate timing.
Definition Routine.hpp:316
@ 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.
Definition Routine.cpp:6
CrossRoutine get_return_object()
Definition Routine.cpp:16
GraphicsRoutine get_return_object()
Creates the GraphicsRoutine object returned to the caller.
Definition Routine.cpp:11