Files
mytheclipse/src/compute.rs
T
asepharyana d8354d6cec Rename crate to mytheclipse and flatten workspace to single crate
- Rename corex -> mytheclipse (corex name taken on crates.io by an
  unrelated crate published by David-Kunz in 2023)
- Flatten corex + corex-core workspace into a single crate; all modules
  (context/error/io/compute/bg) now live directly under src/
- Update Cargo.toml metadata: real repo URL, docs.rs, author
- Update README, example, doc comments, tracing span names
- Fix CI: single package dry-run (corex-core no longer separate)
- Fix publish workflow: single cargo publish step
2026-08-28 16:44:28 +07:00

61 lines
2.1 KiB
Rust

//! Panic-isolated heavy compute on a sized [`rayon::ThreadPool`].
use std::panic::{catch_unwind, AssertUnwindSafe};
use crate::context::context;
use crate::error::CorexError;
/// Runs `f` on the global compute thread pool and returns its result.
///
/// If `f` panics, the panic is caught and converted into
/// [`CorexError::ComputePanic`] instead of unwinding across the pool
/// boundary or poisoning the pool; subsequent calls to [`compute`] continue
/// to work normally.
///
/// # Panic-safety caveat
///
/// `f` is wrapped in [`AssertUnwindSafe`] so that closures capturing
/// ordinary references or non-[`UnwindSafe`](std::panic::UnwindSafe) state
/// can be submitted without a compile error. This is sound with respect to
/// the compute pool itself, since a panicking closure's stack (and any
/// locals it holds) is discarded entirely rather than observed afterward.
/// It does not, however, guarantee exception-safety of state the closure
/// captured by mutable reference: if `f` panics partway through mutating a
/// captured `&mut T`, that `T` may be left in an inconsistent state from
/// the caller's perspective.
pub fn compute<F, R>(f: F) -> Result<R, CorexError>
where
F: FnOnce() -> R + Send,
R: Send,
{
let wrapped = AssertUnwindSafe(f);
context()
.compute_pool
.install(move || catch_unwind(wrapped))
.map_err(|payload| CorexError::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, CorexError> = compute(|| panic!("boom"));
assert!(matches!(panicked, Err(CorexError::ComputePanic(_))));
let recovered = compute(|| 1 + 1);
assert_eq!(recovered.unwrap(), 2);
}
}