Skip to content

Commit

Permalink
Add resume_unwind, à la Rust's version from std::panic (#497)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinaboos committed Mar 17, 2022
1 parent a966924 commit ca960df
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 7 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions applications/unwind_test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ path = "../../kernel/terminal_print"

[dependencies.task]
path = "../../kernel/task"

[dependencies.catch_unwind]
path = "../../kernel/catch_unwind"
39 changes: 33 additions & 6 deletions applications/unwind_test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ extern crate alloc;
#[macro_use] extern crate log;
// #[macro_use] extern crate terminal_print;
extern crate task;
extern crate catch_unwind;


use alloc::vec::Vec;
Expand All @@ -15,7 +16,7 @@ use alloc::string::String;
struct MyStruct(pub usize);
impl Drop for MyStruct {
fn drop(&mut self) {
warn!("DROPPING MYSTRUCT({})", self.0);
warn!("\nDROPPING MYSTRUCT({})\n", self.0);
}
}

Expand All @@ -36,7 +37,7 @@ fn foo(cause_page_fault: bool) {
}


pub fn main(_args: Vec<String>) -> isize {
pub fn main(args: Vec<String>) -> isize {

// // dump some info about the this loaded app crate
// {
Expand All @@ -52,11 +53,37 @@ pub fn main(_args: Vec<String>) -> isize {

let _my_struct = MyStruct(5);

let cause_page_fault = match _args.get(0).map(|s| &**s) {
Some("-e") => true,
_ => false,
match args.get(0).map(|s| &**s) {
// cause a page fault to test unwinding through a machine exception
Some("-e") => foo(true),
// test catch_unwind and then resume_unwind
Some("-c") => catch_resume_unwind(),
_ => foo(false),
};

foo(cause_page_fault);
error!("Test failure: unwind_test::main should not return!");

0
}

#[inline(never)]
fn catch_resume_unwind() {
let _my_struct6 = MyStruct(6);

let res = catch_unwind::catch_unwind_with_arg(fn_to_catch, MyStruct(22));
warn!("CAUGHT UNWINDING ACTION, as expected.");
let _my_struct7 = MyStruct(7);
if let Err(e) = res {
let _my_struct8 = MyStruct(8);
catch_unwind::resume_unwind(e);
}

error!("Test failure: catch_resume_unwind should not return!");
}

#[inline(never)]
fn fn_to_catch(_s: MyStruct) {
let _my_struct9 = MyStruct(9);

panic!("intentional panic in unwind_test::fn_to_catch()")
}
25 changes: 25 additions & 0 deletions kernel/catch_unwind/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,28 @@ fn try_intrinsic_trampoline<F, A, R>(try_intrinsic_arg: *mut u8) where F: FnOnce
);
}
}


/// Resumes the unwinding procedure after it was caught with [`catch_unwind_with_arg()`].
///
/// This is analogous to the Rust's [`std::panic::resume_unwind()`] in that it is
/// intended to be used to continue unwinding after a panic was caught.
///
/// The argument is a [`KillReason`] instead of a typical Rust panic "payload"
/// (which is usually `Box<Any + Send>`) for two reasons:
/// 1. `KillReason` is the type returned by [`catch_unwind_with_arg()`] upon failure,
/// so it makes sense to continue unwinding with that same error type.
/// 2. It's more flexible than the standard Rust panic info type because it must also
/// represent the possibility of a non-panic failure, e.g., a machine exception.
///
/// [`std::panic::resume_unwind()`]: https://doc.rust-lang.org/std/panic/fn.resume_unwind.html
pub fn resume_unwind(caught_panic_reason: KillReason) -> ! {
// We can skip up to 2 frames here: `unwind::start_unwinding` and `resume_unwind` (this function)
let result = unwind::start_unwinding(caught_panic_reason, 2);

// `start_unwinding` should not return
panic!("BUG: start_unwinding() returned {:?}. This is an unexpected failure, as no unwinding occurred. Task: {:?}.",
result,
task::get_my_current_task()
);
}
7 changes: 6 additions & 1 deletion kernel/unwind/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,9 +689,14 @@ fn get_eh_frame_info(crate_ref: &StrongCrateRef) -> Option<(StrongSectionRef, Ba
///
/// # Arguments
/// * `reason`: the reason why the current task is being killed, e.g., due to a panic, exception, etc.
/// * `stack_frames_to_skip`: the number of stack frames that should be skipped in order to avoid unwinding them.
/// * `stack_frames_to_skip`: the number of stack frames that can be skipped in order to avoid unwinding them.
/// Those frames should have nothing that needs to be unwound, e.g., no landing pads that invoke drop handlers.
/// For example, for a panic, the first `5` frames in the call stack can be ignored.
///
/// ## Note: Skipping frames
/// If you are unsure how many frames you could possibly skip, then it's always safe to pass `0`
/// such that all function frames on the stack are unwound.
///
#[doc(hidden)]
pub fn start_unwinding(reason: KillReason, stack_frames_to_skip: usize) -> Result<(), &'static str> {
// Here we have to be careful to have no resources waiting to be dropped/freed/released on the stack.
Expand Down

0 comments on commit ca960df

Please sign in to comment.