Macro rb_sys::rb_gc_guard

source ·
macro_rules! rb_gc_guard {
    ($v:expr) => { ... };
}
Expand description

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:

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

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);