rb_sys/
memory.rs

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