Skip to main content

Module thread

Module thread 

Source
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

§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 stack checking entirely. This is safe because:

  1. The OS still enforces real stack limits
  2. 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§

RThreadBuilder
Builder for spawning threads with R-appropriate stack sizes.

Constants§

DEFAULT_R_STACK_SIZE
Default stack size for R-compatible threads (8 MiB).