Skip to main content

Module thread

Module thread 

Source
Expand description

Advanced controls for R’s process-global C-stack bounds.

R’s stack checking mechanism causes segfaults when R API functions are called from threads other than the main R thread. This module exposes legacy tools that disable that one check, but doing so does not make R’s API safe on a secondary thread.

§R API calls remain main-thread-only

R’s global state, garbage collector, and error signaling are not made thread-safe by changing R_CStackLimit. Writing R Extensions requires package R API calls to stay on R’s main thread and specifically says packages must not change these variables to call stack-checking internals on a secondary thread. Do not use this module as an off-main R bridge in package code. The misleading package-facing surface is tracked for removal or relocation in #1352.

§Prefer crate::worker::with_r_thread in normal code

The supported bridge is crate::worker::with_r_thread, which routes a closure from miniextendr’s dedicated worker context to the recorded R main thread. Arbitrary Rayon or std::thread workers cannot call R directly and cannot use with_r_thread outside that active worker context.

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 currently recognizes this guard as an unchecked-FFI context, but that only reflects the existing API; it does not override R’s main-thread contract.

§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

§Background

R tracks three variables for stack overflow detection (all non-API):

  • R_CStackStart - top of the main thread’s stack
  • R_CStackLimit - stack size limit
  • R_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 R’s own stack-address check. The OS still enforces its real stack limit, but all other R threading invariants remain unchanged.

§Example

use miniextendr_api::thread::StackCheckGuard;

// Advanced embedded-host bookkeeping only; not package R API access.
let _guard = StackCheckGuard::disable();
assert!(miniextendr_api::thread::is_stack_checking_disabled());

§Feature Gate

This module requires the nonapi feature because it accesses non-API R internals (R_CStackLimit, R_CStackStart, R_CStackDir).

Structs§

RThreadBuilder
Builder for spawning pure-Rust threads with a legacy R-sized stack.

Constants§

DEFAULT_R_STACK_SIZE
Default stack size for the legacy R-sized thread builder (8 MiB).