Project

General

Profile

Ractor-safe C extension

Ractor is parallel execution mechanism introduced from Ruby 3.0. All ractrors can run in parallel by different OS thread (underlying system provided thread), so the C extension should be thread-safe. Now we call the property that C extension can run in multiple ractors "Ractor-safe".

By default, all C extensions are recognized as Ractor-unsafe. If C extension becomes Ractor-safe, the extension should call rb_ext_ractor_safe(true) at the Init_ function and all defined method marked as Ractor-safe. Ractor-unsafe C-methods only been called from main-ractor. If non-main ractor calls it, then Ractor::UnsafeError is raised.

To make "Ractor-safe" C extension, we need to check the following points:

(1) Do not share unshareable objects between ractors

For example, C's global variable can lead sharing a value between Ractors.

VALUE g_var;
VALUE set(VALUE self, VALUE v){ return g_var = v; }
VALUE get(VALUE self){ return g_var; }

set() and get() pair can share an unshareable objects using g_var, and it is Ractor-unsafe.

Not only global variables directly, some indirect data structure such as global st_table can share the objects, so please take care.

Note that class and module objects are shareable objects, so you can keep the code "cFoo = rb_define_class(...)" with C's global variables.

(2) Check the thread-safety of the extension

An extension should be thread-safe. For example, the following code is not thread-safe:

bool g_called = false;
VALUE call(VALUE self) {
if (g_called) rb_raise("recursive call is not allowed.");
g_called = true;
VALUE ret = do_something();
g_called = false;
return ret;
}

Because g_called should be synchronized by other ractor's threads. To avoid such data-race, some synchronization should be used. Check include/ruby/thread_native.h.

(3) Check the thread-safety of using library.

If an extension relies on the external library libfoo and the function foo(), the function foo() should be thread safe.

(4) Make an object shareable

This is not required to make an extension Ractor-safe.

If an extension provides special objects defined by rb_data_type_t, consider these objects can become shareable or not.

RUBY_TYPED_FROZEN_SHAREABLE flag indicates that these objects can be shareable objects if the object is frozen. This means that if the object is frozen, the mutation of wrapped data is not allowed.

(5) Others

Maybe there are more points which should be considered. See