task_lifecycle/
task_lifecycle.rs1#![cfg_attr(
21 not(bela_device),
22 allow(
23 dead_code,
24 reason = "only the fallback main is reachable off-device; the application code should still compile and lint"
25 )
26)]
27
28use core::sync::atomic::{AtomicU64, Ordering};
29#[cfg(not(bela_device))]
30use std::process::ExitCode;
31use std::sync::{Arc, Mutex, PoisonError};
32
33use bela::{
34 AuxiliaryTask, BelaApplication, BlockContext, CleanupContext, Error, Priority, RenderContext,
35 SetupContext, ThreadInfo, rt_println,
36};
37
38const TASK_PRIORITY: Priority = Priority::new(50).expect("50 is within Bela's priority range");
39
40struct Abandoned {
43 runs: Arc<AtomicU64>,
44 handle: Arc<Mutex<Option<AuxiliaryTask>>>,
45}
46
47impl BelaApplication for Abandoned {
48 type RenderState = ();
49
50 fn setup(&mut self, _context: &SetupContext) -> bool {
51 let runs = Arc::clone(&self.runs);
52 match AuxiliaryTask::new("bela-rs-abandoned", TASK_PRIORITY, move || {
53 runs.fetch_add(1, Ordering::Relaxed);
54 }) {
55 Ok(task) => {
56 *self.handle.lock().unwrap_or_else(PoisonError::into_inner) = Some(task);
59 true
60 }
61 Err(error) => {
62 rt_println!("lifecycle: could not create the abandoned task: {error}");
63 false
64 }
65 }
66 }
67
68 fn create_render_state(&mut self, _thread: ThreadInfo, _context: &SetupContext) {}
69
70 fn render(&self, _state: &mut (), _context: &mut RenderContext) {}
71}
72
73struct Survivor {
75 stale: Option<AuxiliaryTask>,
76 stale_runs: Arc<AtomicU64>,
77 fresh: Option<AuxiliaryTask>,
78 fresh_runs: Arc<AtomicU64>,
79 blocks: u64,
80 interval: u64,
81}
82
83impl BelaApplication for Survivor {
84 type RenderState = ();
85
86 fn setup(&mut self, context: &SetupContext) -> bool {
87 #[allow(
88 clippy::cast_possible_truncation,
89 clippy::cast_sign_loss,
90 reason = "the sample rate is a small positive number"
91 )]
92 let sample_rate_hz = context.audio_sample_rate() as u64;
93 self.interval = (sample_rate_hz / context.audio_frames().max(1) as u64).max(1);
94
95 let runs = Arc::clone(&self.fresh_runs);
96 self.fresh = AuxiliaryTask::new("bela-rs-survivor", TASK_PRIORITY, move || {
97 runs.fetch_add(1, Ordering::Relaxed);
98 })
99 .ok();
100 self.fresh.is_some()
101 }
102
103 fn create_render_state(&mut self, _thread: ThreadInfo, _context: &SetupContext) {}
104
105 fn render(&self, _state: &mut (), _context: &mut RenderContext) {}
106
107 fn render_post(&mut self, _states: &mut [()], context: &mut BlockContext) {
110 self.blocks += 1;
111 if self.blocks % self.interval != 0 {
112 return;
113 }
114 if let Some(stale) = &self.stale {
115 stale.schedule(context);
116 }
117 if let Some(fresh) = &self.fresh {
118 fresh.schedule(context);
119 }
120 }
121
122 fn cleanup(&mut self, _states: &mut [()], _context: &CleanupContext) {
123 let created = AuxiliaryTask::new("bela-rs-in-cleanup", TASK_PRIORITY, || {});
125 let cleanup_create = match created {
126 Err(Error::TaskCreateWhileStopping) => "rejected",
127 Err(_) => "failed-otherwise",
128 Ok(_) => "created",
129 };
130 rt_println!(
131 "lifecycle: stale-runs={} fresh-runs={} cleanup-create={}",
132 self.stale_runs.load(Ordering::Relaxed),
133 self.fresh_runs.load(Ordering::Relaxed),
134 cleanup_create
135 );
136 }
137}
138
139#[cfg(bela_device)]
140fn main() -> Result<(), Error> {
141 use bela::{Bela, Settings};
142
143 let stale_runs = Arc::new(AtomicU64::new(0));
144 let handle = Arc::new(Mutex::new(None));
145
146 drop(Bela::new(
149 Abandoned {
150 runs: Arc::clone(&stale_runs),
151 handle: Arc::clone(&handle),
152 },
153 &Settings::new(),
154 )?);
155 rt_println!("lifecycle: abandoned audio system dropped without starting");
156
157 let stale = handle.lock().unwrap_or_else(PoisonError::into_inner).take();
158 Bela::run(
159 Survivor {
160 stale,
161 stale_runs,
162 fresh: None,
163 fresh_runs: Arc::new(AtomicU64::new(0)),
164 blocks: 0,
165 interval: 1,
166 },
167 &Settings::new(),
168 )
169}
170
171#[cfg(not(bela_device))]
172fn main() -> ExitCode {
173 eprintln!("This example must be cross-compiled for Bela Gem (aarch64-unknown-linux-gnu).");
174 ExitCode::FAILURE
175}