use std::panic::{catch_unwind, AssertUnwindSafe};
use crate::context::context;
use crate::error::MytheclipseError;
pub fn compute<F, R>(f: F) -> Result<R, MytheclipseError>
where
F: FnOnce() -> R + Send,
R: Send,
{
let wrapped = AssertUnwindSafe(f);
context()
.compute_pool
.install(move || catch_unwind(wrapped))
.map_err(|payload| MytheclipseError::ComputePanic(panic_payload_to_string(payload)))
}
fn panic_payload_to_string(payload: Box<dyn std::any::Any + Send>) -> String {
if let Some(message) = payload.downcast_ref::<&str>() {
(*message).to_string()
} else if let Some(message) = payload.downcast_ref::<String>() {
message.clone()
} else {
"compute closure panicked with a non-string payload".to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compute_panic_is_isolated_and_pool_survives() {
let panicked: Result<u32, MytheclipseError> = compute(|| panic!("boom"));
assert!(matches!(panicked, Err(MytheclipseError::ComputePanic(_))));
let recovered = compute(|| 1 + 1);
assert_eq!(recovered.unwrap(), 2);
}
}