uefi_corosensei/
lib.rs

1//! ## Overview
2//!
3//! This crate provides a safe and efficient abstraction for context switching between different stacks, in the form of [coroutines]. A coroutine is a function that can be paused and resumed, yielding values to the caller. A coroutine can suspend itself from any point in its call stack. In addition to receiving yielded values from a coroutine, you can also pass data into the coroutine each time it is resumed.
4//!
5//! [coroutines]: https://en.wikipedia.org/wiki/Coroutine
6//!
7//! ## Example
8//!
9//! ```rust
10//! use corosensei::{Coroutine, CoroutineResult};
11//!
12//! fn main() {
13//!     println!("[main] creating coroutine");
14//!
15//!     let mut coroutine = Coroutine::new(|yielder, input| {
16//!         println!("[coroutine] coroutine started with input {}", input);
17//!         for i in 0..5 {
18//!             println!("[coroutine] yielding {}", i);
19//!             let input = yielder.suspend(i);
20//!             println!("[coroutine] got {} from parent", input)
21//!         }
22//!         println!("[coroutine] exiting coroutine");
23//!     });
24//!
25//!     let mut counter = 100;
26//!     loop {
27//!         println!("[main] resuming coroutine with argument {}", counter);
28//!         match coroutine.resume(counter) {
29//!             CoroutineResult::Yield(i) => println!("[main] got {:?} from coroutine", i),
30//!             CoroutineResult::Return(()) => break,
31//!         }
32//!
33//!         counter += 1;
34//!     }
35//!
36//!     println!("[main] exiting");
37//! }
38//! ```
39//!
40//! #### Output
41//!
42//! ```text
43//! [main] creating coroutine
44//! [main] resuming coroutine with argument 100
45//! [coroutine] coroutine started with input 100
46//! [coroutine] yielding 0
47//! [main] got 0 from coroutine
48//! [main] resuming coroutine with argument 101
49//! [coroutine] got 101 from parent
50//! [coroutine] yielding 1
51//! [main] got 1 from coroutine
52//! [main] resuming coroutine with argument 102
53//! [coroutine] got 102 from parent
54//! [coroutine] yielding 2
55//! [main] got 2 from coroutine
56//! [main] resuming coroutine with argument 103
57//! [coroutine] got 103 from parent
58//! [coroutine] yielding 3
59//! [main] got 3 from coroutine
60//! [main] resuming coroutine with argument 104
61//! [coroutine] got 104 from parent
62//! [coroutine] yielding 4
63//! [main] got 4 from coroutine
64//! [main] resuming coroutine with argument 105
65//! [coroutine] got 105 from parent
66//! [coroutine] exiting coroutine
67//! [main] exiting
68//! ```
69//!
70//! ## Supported targets
71//!
72//! This crate currently supports the following targets:
73//!
74//! |         | ELF (Linux, BSD, bare metal, etc) | Darwin (macOS, iOS, etc) | Windows |
75//! |---------|-----------------------------------|--------------------------|---------|
76//! | x86_64  | ✅                                 | ✅                        | ✅       |
77//! | x86     | ✅                                 | ✅                        | ⚠️*      |
78//! | AArch64 | ✅                                 | ✅                        | ❌       |
79//! | ARM     | ✅                                 | ❌                        | ❌       |
80//! | RISC-V  | ✅                                 | ❌                        | ❌       |
81//!
82//! *\* Linked backtraces are not supported on x86 Windows.*
83//!
84//! Feel free to open an issue if your target is not supported yet.
85//!
86//! ## Features
87//!
88//! #### Panic propagation
89//!
90//! If a panic occurs in a coroutine then the panic will unwind through the coroutine stack and then continue to unwind out of the caller which last resumed it. Once this has happened, the coroutine is considered complete and can no longer be resumed.
91//!
92//! ```rust
93//! use std::panic::{catch_unwind, AssertUnwindSafe};
94//!
95//! use corosensei::Coroutine;
96//!
97//! fn main() {
98//!     println!("[main] creating coroutine");
99//!
100//!     let mut coroutine = Coroutine::new(|yielder, ()| {
101//!         println!("[coroutine] yielding 42");
102//!         yielder.suspend(42);
103//!
104//!         println!("[coroutine] panicking");
105//!         panic!("foobar");
106//!     });
107//!
108//!     println!("[main] resuming coroutine");
109//!     let result = catch_unwind(AssertUnwindSafe(|| coroutine.resume(())));
110//!     println!(
111//!         "[main] got value {} from coroutine",
112//!         result.unwrap().as_yield().unwrap()
113//!     );
114//!
115//!     println!("[main] resuming coroutine");
116//!     let result = catch_unwind(AssertUnwindSafe(|| coroutine.resume(())));
117//!     println!(
118//!         "[main] caught panic \"{}\" from coroutine",
119//!         result.unwrap_err().downcast_ref::<&'static str>().unwrap()
120//!     );
121//!
122//!     println!("[main] exiting");
123//! }
124//! ```
125//!
126//! ```text
127//! [main] creating coroutine
128//! [main] resuming coroutine
129//! [coroutine] yielding 42
130//! [main] got value 42 from coroutine
131//! [main] resuming coroutine
132//! [coroutine] panicking
133//! thread 'main' panicked at 'foobar', examples/panic.rs:13:9
134//! note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
135//! [main] caught panic "foobar" from coroutine
136//! [main] exiting
137//! ```
138//!
139//! #### Linked backtraces
140//!
141//! Backtraces taken from within a coroutine will continue into the parent stack from the point where they were the coroutine was last resumed from. This is a significant help towards debugging issues with code in coroutines.
142//!
143//! Notice how the backtrace in this example shows that the coroutine was last resumed from `sub_function`:
144//!
145//! ```rust
146//! use backtrace::Backtrace;
147//! use corosensei::Coroutine;
148//!
149//! #[inline(never)]
150//! fn sub_function(coroutine: &mut Coroutine<(), (), ()>) {
151//!     coroutine.resume(());
152//! }
153//!
154//! fn main() {
155//!     let mut coroutine = Coroutine::new(move |_, ()| {
156//!         let trace = Backtrace::new();
157//!         println!("{:?}", trace);
158//!     });
159//!
160//!     sub_function(&mut coroutine);
161//! }
162//! ```
163//!
164//! ```text
165//!    0: backtrace::main::{{closure}}
166//!              at examples/backtrace.rs:11:21
167//!       corosensei::coroutine::ScopedCoroutine<Input,Yield,Return,Stack>::with_stack::coroutine_func::{{closure}}
168//!              at src/coroutine.rs:174:62
169//!       corosensei::unwind::catch_unwind_at_root
170//!              at src/unwind.rs:43:16
171//!    1: corosensei::coroutine::ScopedCoroutine<Input,Yield,Return,Stack>::with_stack::coroutine_func
172//!              at src/coroutine.rs:174:30
173//!    2: stack_init_trampoline
174//!    3: corosensei::arch::x86_64::switch_and_link
175//!              at src/arch/x86_64.rs:302:5
176//!       corosensei::coroutine::ScopedCoroutine<Input,Yield,Return,Stack>::resume_inner
177//!              at src/coroutine.rs:256:13
178//!       corosensei::coroutine::ScopedCoroutine<Input,Yield,Return,Stack>::resume
179//!              at src/coroutine.rs:235:19
180//!       backtrace::sub_function
181//!              at examples/backtrace.rs:6:5
182//!    4: backtrace::main
183//!              at examples/backtrace.rs:15:5
184//! ```
185//!
186//! Linked backtraces use the platform's standard unwinding metadata and work with debuggers, profilers and many other tools.
187//!
188//! #### Cleanup on drop
189//!
190//! If a coroutine is dropped while it is suspended then the coroutine's stack will be safely unwound using the same mechanism as panics, which will drop any local variables on the stack. This is necessary to maintain a safe API: the coroutine's stack cannot be freed or reused while there are still live objects on it.
191//!
192//! If this cleanup is not desired then a coroutine can forcibly be marked as completed using the unsafe `force_reset` function.
193//!
194//! #### Trap handling
195//!
196//! For advanced use cases, this crate provides the ability to recover from traps (e.g. segmentation fault, stack overflow) that occur in a coroutine. This API is unsafe and should be used from within a signal/exception handler: it will help set up the return from the signal/exception handler so that the coroutine will exit immediately with the given return value and return control to its parent.
197//!
198//! This is particularly useful for running JIT-compiled code with a fixed stack limit: stack overflows can be caught by a signal/exception handler and easily recovered from.
199//!
200//! ## Cargo features
201//!
202//! This crate is compatible with `#![no_std]` when all Cargo features are disabled.
203//!
204//! The following Cargo feature flags are available on this crate:
205//!
206//! #### `default-stack` (Enabled by default)
207//!
208//! This feature provides a `DefaultStack` implementation which can be used by the `Coroutine` type. This stack is allocated with a guard page using OS APIs and requires `std`.
209//!
210//! If this feature is disabled then you must implement your own stack type which implements the `Stack` trait.
211//!
212//! #### `unwind` (Enabled by default)
213//!
214//! This feature adds support for:
215//! - unwinding panics in a coroutine back out to its caller.
216//! - forcibly unwinding a suspended coroutine via `force_unwind` or when the coroutine is dropped.
217//!
218//! Note that if a coroutine is dropped while suspended (i.e. it has been resumed at least once but has not returned yet) when this feature is disabled then the program will abort.
219//!
220//! Requires `std`.
221//!
222//! #### `asm-unwind`
223//!
224//! This feature uses the `asm_unwind` nightly feature to allow panics to unwind directly through the inline assembly used in this crate, which can improve performance since it doesn't need to be passed across stack boundaries as a `Result`.
225//!
226//! Implies `unwind`.
227
228#![no_std]
229#![cfg_attr(feature = "asm-unwind", feature(asm_unwind, c_unwind, asm_sym))]
230#![warn(missing_docs)]
231
232// Must come first because it defines macros used by other modules.
233mod unwind;
234
235mod arch;
236mod coroutine;
237pub mod stack;
238pub mod trap;
239mod util;
240
241pub use coroutine::*;
242
243#[cfg(test)]
244mod tests;