Skip to main content

rscad_csg_traits/
stack.rs

1//! Big-stack execution for deep CSG recursion.
2
3/// Stack size for [`on_csg_stack`] workers. Reserved lazily (virtual pages),
4/// so only pages actually touched cost physical memory.
5#[cfg(not(target_arch = "wasm32"))]
6const CSG_STACK_SIZE: usize = 64 << 20;
7
8/// Run `f` on a dedicated thread with a stack large enough for deep CSG
9/// recursion, blocking until it finishes.
10///
11/// BSP build/clip/invert (and the tree's `Drop`) recurse per node, so stack
12/// depth scales with polygon count. Callers can sit on small stacks — the
13/// iOS main thread gets 1 MiB and secondary Apple threads 512 KiB — where a
14/// full-scene CSG conversion overflows (SIGSEGV). Wrap the conversion in
15/// this helper whenever the caller's stack size isn't under our control.
16pub fn on_csg_stack<R: Send>(f: impl FnOnce() -> R + Send) -> R {
17    // wasm32 is single-threaded: no worker thread to spawn, so run inline on
18    // the caller's stack. Deep BSP recursion can overflow the default wasm
19    // stack on complex scenes; bump it at link time with
20    // `-C link-arg=-zstack-size=<bytes>` if that becomes a problem.
21    #[cfg(target_arch = "wasm32")]
22    {
23        f()
24    }
25    #[cfg(not(target_arch = "wasm32"))]
26    {
27        std::thread::scope(|scope| {
28            let handle = std::thread::Builder::new()
29                .name("csg-worker".into())
30                .stack_size(CSG_STACK_SIZE)
31                .spawn_scoped(scope, f)
32                .expect("BUG: failed to spawn csg-worker thread");
33            match handle.join() {
34                Ok(value) => value,
35                Err(panic) => std::panic::resume_unwind(panic),
36            }
37        })
38    }
39}