1use std::sync::{Arc, Mutex};
4
5use sva_core::{Diagnostic, error_envelope};
6
7#[derive(Debug)]
8pub enum Stopped {
9 Panicked(String),
10 NoWorker(String),
11}
12
13pub fn caught<T: Send + 'static>(
15 stack_bytes: usize,
16 call: impl FnOnce() -> T + Send + 'static,
17) -> Result<T, Stopped> {
18 let held = Arc::new(Mutex::new(String::new()));
19 let seen = Arc::clone(&held);
20 let before = std::panic::take_hook();
21 std::panic::set_hook(Box::new(move |info| {
22 *seen.lock().unwrap_or_else(|e| e.into_inner()) = info.to_string();
23 }));
24 let ran = match std::thread::Builder::new()
25 .stack_size(stack_bytes)
26 .spawn(call)
27 {
28 Ok(worker) => worker
29 .join()
30 .map_err(|_| Stopped::Panicked(held.lock().unwrap_or_else(|e| e.into_inner()).clone())),
31 Err(e) => Err(Stopped::NoWorker(format!(
32 "no thread with a {stack_bytes}-byte stack: {e}"
33 ))),
34 };
35 std::panic::set_hook(before);
36 ran
37}
38
39pub fn stopped(why: &Stopped) -> String {
41 let (message, code, text, help) = match why {
42 Stopped::Panicked(text) => (
43 "sva-cli panicked",
44 "cli.panicked",
45 text,
46 "this is a defect in sva-cli, not in the composition; report it with the command \
47 that reached it",
48 ),
49 Stopped::NoWorker(text) => (
50 "sva-cli could not start its worker",
51 "cli.no_worker",
52 text,
53 "the machine refused the thread this CLI runs on; raise the process's memory or \
54 thread limit and run it again",
55 ),
56 };
57 error_envelope(
58 "internal_error",
59 message,
60 &[Diagnostic::new(code, text.clone()).helped(help)],
61 )
62}