Skip to content
File

Blob: firmware/crates/esp32-radio/src/app.rs

rust98 lines
1//! Task ownership and terminal recovery. All ordinary state is explicitly owned.
2use crate::{
3 analysis, console,
4 error::{Error, Result},
5 platform::{self, Board, SpawnConfig},
6 radio, signaling,
7};
8use std::{
9 sync::{
10 atomic::{AtomicBool, Ordering},
11 mpsc::sync_channel,
12 },
13 thread,
14 time::Duration,
15};
16 
17pub(crate) fn run() -> Result<()> {
18 platform::log("Pocket Radio: Rust application starting");
19 let board = Board::init()?;
20 let (analysis, analysis_worker) = analysis::channel();
21 {
22 let _configuration = SpawnConfig::analysis()?;
23 thread::Builder::new()
24 .name("analysis".into())
25 .stack_size(20_480)
26 .spawn(move || {
27 if let Err(error) = analysis_worker.run() {
28 recover(error);
29 }
30 })
31 .map_err(|_| Error::new("cannot start analysis task"))?;
32 }
33 let (metrics, metrics_worker) = crate::metrics::channel();
34 {
35 let _configuration = SpawnConfig::metrics()?;
36 thread::Builder::new()
37 .name("metrics".into())
38 .stack_size(8192)
39 .spawn(move || {
40 if let Err(error) = metrics_worker.run() {
41 platform::log(&format!("Hardware metrics unavailable: {error}"));
42 }
43 })
44 .map_err(|_| Error::new("cannot start metrics task"))?;
45 }
46 let station = crate::station::Station::default();
47 let radio_station = station.clone();
48 let (requests, radio_requests) = sync_channel(4);
49 let (radio_events, events) = sync_channel(8);
50 {
51 let _configuration = SpawnConfig::radio()?;
52 thread::Builder::new()
53 .name("radio".into())
54 .stack_size(49_152)
55 .spawn(move || {
56 if let Err(error) = radio::run(
57 board,
58 radio_requests,
59 radio_events,
60 analysis,
61 radio_station,
62 metrics,
63 ) {
64 recover(error);
65 }
66 })
67 .map_err(|_| Error::new("cannot start radio task"))?;
68 }
69 thread::Builder::new()
70 .name("signaling".into())
71 .stack_size(16_384)
72 .spawn(move || {
73 if let Err(error) = signaling::run(requests, events, station) {
74 recover(error);
75 }
76 })
77 .map_err(|_| Error::new("cannot start signaling task"))?;
78 console::run()
79}
80 
81static RECOVERING: AtomicBool = AtomicBool::new(false);
82 
83/// Fatal runtime failures restart the device. One caller owns
84/// the recovery counter; concurrent failures park until reset, avoiding races.
85pub(crate) fn recover(error: Error) -> ! {
86 if RECOVERING.swap(true, Ordering::AcqRel) {
87 loop {
88 thread::sleep(Duration::from_secs(1));
89 }
90 }
91 let seconds = 5u64 << platform::recovery_attempt(false).min(3);
92 platform::log(&format!("{error}; restarting in {seconds} seconds"));
93 thread::sleep(Duration::from_millis(
94 seconds * 1000 + u64::from(platform::random() % 1000),
95 ));
96 platform::restart()
97}