Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pass return values from the closure to the caller #13

Merged
merged 3 commits into from
Oct 3, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 42 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![cfg_attr(not(feature = "std"), no_std)]
use core::ffi::c_void;
use core::{ffi::c_void, mem::MaybeUninit};

mod code;
mod exception;
Expand Down Expand Up @@ -40,16 +40,34 @@ where
}
}

fn do_execute_proc<F>(mut closure: F) -> Result<(), Exception>
where
F: FnMut(),
{
let mut exception = Exception::empty();
let closure = &mut closure as *mut _ as *mut c_void;

unsafe {
match handler_stub(handled_proc::<F>, closure, &mut exception) {
MS_CATCHED => Err(exception),
MS_DISABLED => panic!("exception handling is not supported in this build of microseh"),
/* MS_SUCCEEDED */ _ => Ok(()),
}
}
}

/// Executes the provided closure in a context where exceptions are handled, catching any\
/// hardware exceptions that occur.
///
/// Any value returned by the closure is returned by this function, if no exceptions occur.
///
/// # Arguments
///
/// * `closure` - The closure or function to be executed within the handled context.
///
/// # Returns
///
/// * `Ok(())` - If the closure executed without throwing any exceptions.
/// * `Ok(R)` - If the closure executed without throwing any exceptions.
/// * `Err(Exception)` - If an exception occurred during the execution of the closure.
///
/// # Examples
Expand All @@ -67,7 +85,7 @@ where
/// # Caveats
///
/// If an exception occours within the closure, resources that require cleanup via\
/// the `Drop` trait, may not be properly released.
/// the `Drop` trait, will not be released.
///
/// As a rule of thumb, it's recommended not to define resources that implement\
/// the `Drop` trait inside the closure. Instead, allocate and manage these resources\
Expand All @@ -77,20 +95,17 @@ where
///
/// If exception handling is disabled in the build, which occurs when the library is\
/// not built on Windows with Microsoft Visual C++.
pub fn try_seh<F>(mut closure: F) -> Result<(), Exception>
pub fn try_seh<F, R>(mut closure: F) -> Result<R, Exception>
where
F: FnMut(),
F: FnMut() -> R,
{
let mut exception = Exception::empty();
let closure = &mut closure as *mut _ as *mut c_void;

unsafe {
match handler_stub(handled_proc::<F>, closure, &mut exception) {
MS_CATCHED => Err(exception),
MS_DISABLED => panic!("exception handling is not supported in this build of microseh"),
/* MS_SUCCEEDED */ _ => Ok(()),
}
}
let mut ret_val = MaybeUninit::<R>::uninit();
do_execute_proc(|| {
ret_val.write(closure());
})
// SAFETY: We should only reach this point if the inner closure has returned
// without throwing an exception, so `ret_val` should be initialized.
.map(|_| unsafe { ret_val.assume_init() })
}

#[cfg(test)]
Expand Down Expand Up @@ -230,4 +245,16 @@ mod tests {

assert_eq!(ex.unwrap_err().registers().x0(), 0xbadc0debabefffff);
}

#[test]
fn ret_vals() {
let a = try_seh(|| 1337);
assert_eq!(a.unwrap(), 1337);

let b = try_seh(|| "hello");
assert_eq!(b.unwrap(), "hello");

let c = try_seh(|| {});
assert_eq!(core::mem::size_of_val(&c.unwrap()), 0x0);
}
}
Loading