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