vexide_core/program.rs
1//! User program state.
2//!
3//! This module provides functionality for controlling the state of the currently running
4//! user program on the Brain. At the time, that is essentially just functionality for
5//! exiting the program early using the [`abort`] and [`exit`] functions.
6
7use core::{convert::Infallible, fmt::Debug, time::Duration};
8
9use vex_sdk::{vexSerialWriteFree, vexSystemExitRequest, vexTasksRun};
10
11use crate::{io, time::Instant};
12
13/// A trait that can be implemented for arbitrary return types in the `main` function.
14pub trait Termination {
15 /// Run specific termination logic.
16 ///
17 /// Unlike in the standard library, this function does not return a status code.
18 fn report(self);
19}
20impl Termination for () {
21 fn report(self) {}
22}
23impl Termination for ! {
24 fn report(self) {}
25}
26impl Termination for Infallible {
27 fn report(self) {}
28}
29impl<T: Termination, E: Debug> Termination for Result<T, E> {
30 fn report(self) {
31 match self {
32 Ok(t) => t.report(),
33 Err(e) => io::println!("Error: {e:?}"),
34 }
35 }
36}
37
38const FLUSH_TIMEOUT: Duration = Duration::from_millis(15);
39
40/// Exits the program using `vexSystemExitRequest`.
41///
42/// This function will block up to 15mS to allow the serial buffer to flush, then either exit the program or
43/// block for an indefinite amount of time depending on how long it takes VEXos to kill the program.
44///
45/// Note that because this function never returns, and that it terminates the process, no destructors on
46/// the current stack will be run. If a clean shutdown is needed it is recommended to only call this function
47/// at a known point where there are no more destructors left to run; or, preferably, simply return a type
48/// implementing [`Termination`] (such as `Result`) from the `main` function and avoid this function altogether:
49///
50/// ```
51/// #[vexide::main]
52/// async fn main(peripherals: Peripherals) -> Result<(), MyError> {
53/// // ...
54/// Ok(())
55/// }
56/// ```
57pub fn exit() -> ! {
58 let exit_time = Instant::now();
59
60 unsafe {
61 // Force the serial buffer to flush
62 while exit_time.elapsed() < FLUSH_TIMEOUT {
63 vexTasksRun();
64
65 // If the buffer has been fully flushed, exit the loop
66 if vexSerialWriteFree(io::STDIO_CHANNEL) == (io::Stdout::INTERNAL_BUFFER_SIZE as i32) {
67 break;
68 }
69 }
70
71 // Request program exit.
72 vexSystemExitRequest();
73
74 // Loop while vexos decides what to do with our exit request.
75 loop {
76 vexTasksRun();
77 }
78 }
79}
80
81/// Exits the program using `vexSystemExitRequest` without flushing the serial buffer.
82///
83/// This function is virtually identical to [`exit`] with the caveat that it will not wait for
84/// stdio buffers to be printed, meaning any writes to `Stdout` *MAY* not be written before the
85/// program is killed by VEXos.
86pub fn abort() -> ! {
87 unsafe {
88 // Request program exit from CPU0.
89 vexSystemExitRequest();
90
91 // Spin while CPU0 decides what to do with our exit request.
92 loop {
93 vexTasksRun();
94 }
95 }
96}