Expand description
Thread safety utilities for calling R from non-main threads.
R’s stack checking mechanism causes segfaults when R API functions are called from threads other than the main R thread. This module provides utilities to safely disable stack checking when crossing thread boundaries.
§Prefer crate::worker::with_r_thread in normal code
This module is the escape hatch for advanced cases (rayon pools,
externally-spawned threads that need to touch R briefly). The default
tool for crossing back to R is crate::worker::with_r_thread, which
routes the closure to the recorded main thread instead of unsafely
patching R’s internal stack bounds.
StackCheckGuard is gated behind the nonapi feature because it
mutates R_CStackStart / R_CStackLimit / R_CStackDir, none of which
are part of R’s public C API. The lint MXL301 treats this guard the
same way as the other known-safe contexts — inside a StackCheckGuard
scope you may use *_unchecked FFI variants because the main-thread
assertion would be wrong (you’re explicitly not on the main thread).
§Don’t use Rf_error here either
A longjmp from a non-main thread is undefined behaviour even with the
stack check disabled. Panic, capture the message in your guard’s
fallback (see crate::ffi_guard::guarded_ffi_call_with_fallback), and
surface the failure to the main thread before letting R see it. The lint
MXL300 rejects direct Rf_error calls in user code.
§Cross references
crate::worker::with_r_thread— preferred path for crossing back to R.crate::sys— checked vs*_uncheckedFFI surface.
§Background
R tracks three variables for stack overflow detection (all non-API):
R_CStackStart- top of the main thread’s stackR_CStackLimit- stack size limitR_CStackDir- stack growth direction
When R API functions check the stack, they compare the current stack pointer against these bounds. On a different thread, the stack is completely different, causing false stack overflow detection.
§Solution
Setting R_CStackLimit to usize::MAX disables stack checking entirely.
This is safe because:
- The OS still enforces real stack limits
- R will still function correctly, just without its own overflow detection
§Example
use miniextendr_api::thread::StackCheckGuard;
std::thread::spawn(|| {
// This would segfault without the guard!
let _guard = StackCheckGuard::disable();
// Now safe to call R APIs
unsafe { miniextendr_api::SEXP::scalar_integer_unchecked(42) };
// Guard restores original limit on drop
});§Feature Gate
This module requires the nonapi feature because it accesses non-API
R internals (R_CStackLimit, R_CStackStart, R_CStackDir).
Structs§
- RThread
Builder - Builder for spawning threads with R-appropriate stack sizes.
Constants§
- DEFAULT_
R_ STACK_ SIZE - Default stack size for R-compatible threads (8 MiB).