rb_sys/
memory.rs

1/// Prevents premature destruction of local objects.
2///
3/// Ruby's garbage collector is conservative; it scans the C level machine stack as well.
4/// Possible in-use Ruby objects must remain visible on stack, to be properly marked as such.
5/// However, Rust's compiler optimizations might remove the references to these objects from
6/// the stack when they are not being used directly.
7///
8/// Consider the following example:
9///
10/// ```ignore
11/// use rb_sys::{rb_str_new_cstr, rb_str_cat_cstr, RSTRING_PTR, rb_gc_guard};
12///
13/// unsafe {
14///     let s = rb_str_new_cstr(" world\0".as_ptr() as _);
15///     let sptr = RSTRING_PTR(s);
16///     let t = rb_str_new_cstr("hello,\0".as_ptr() as _); // Possible GC invocation
17///     let u = rb_str_cat_cstr(t, sptr);
18///     rb_gc_guard!(s); // ensure `s` (and thus `sptr`) do not get GC-ed
19/// }
20/// ```
21///
22/// In this example, without the `rb_gc_guard!`, the last use of `s` is before the last use
23/// of `sptr`. Compilers could think `s` and `t` are allowed to overlap. That would
24/// eliminate `s` from the stack, while `sptr` is still in use. If our GC runs at that
25/// very moment, `s` gets swept out, which also destroys `sptr`.
26///
27/// In order to prevent this scenario, `rb_gc_guard!` must be placed after the last use
28/// of `sptr`. Placing `rb_gc_guard!` before dereferencing `sptr` would be of no use.
29///
30/// Using the `rb_gc_guard!` macro has the following advantages:
31///
32/// - the intent of the macro use is clear.
33///
34/// - `rb_gc_guard!` only affects its call site, without negatively affecting other systems.
35///
36/// # Example
37/// ```no_run
38/// use rb_sys::{rb_utf8_str_new_cstr, rb_gc_guard};
39///
40/// let my_string = unsafe { rb_utf8_str_new_cstr("hello world\0".as_ptr() as _) };
41/// let _ = rb_gc_guard!(my_string);
42/// ```
43#[macro_export]
44macro_rules! rb_gc_guard {
45    ($v:expr) => {{
46        unsafe {
47            let val: $crate::VALUE = $v;
48            let rb_gc_guarded_ptr = std::ptr::read_volatile(&&val);
49            std::arch::asm!("/* {0} */", in(reg) rb_gc_guarded_ptr);
50            *rb_gc_guarded_ptr
51        }
52    }};
53}