diff options
author | Jean Boussier <[email protected]> | 2025-06-24 12:33:27 +0200 |
---|---|---|
committer | Jean Boussier <[email protected]> | 2025-06-24 15:10:40 +0100 |
commit | 5bcc639b341291fe0584d11c6bdd1add29f40087 (patch) | |
tree | 999a2b0dc29e12fb2964a862ac48af68f0203ad5 | |
parent | 45a2c95d0f7184c9cd64ddd26699af31bea8675d (diff) |
Disallow forking from non-main ractor
[Bug #17516]
`fork(2)` only leave the calling thread alive in the child.
Because of this forking from the non-main ractor can easily
leave the VM in a corrupted state.
It may be possible in the future to carefully allow forking from non-main
Ractor, but shot term it's preferable to add this restriction.
-rw-r--r-- | common.mk | 1 | ||||
-rw-r--r-- | process.c | 5 | ||||
-rw-r--r-- | test/ruby/test_ractor.rb | 11 |
3 files changed, 17 insertions, 0 deletions
@@ -14057,6 +14057,7 @@ process.$(OBJEXT): {$(VPATH)}onigmo.h process.$(OBJEXT): {$(VPATH)}oniguruma.h process.$(OBJEXT): {$(VPATH)}process.c process.$(OBJEXT): {$(VPATH)}ractor.h +process.$(OBJEXT): {$(VPATH)}ractor_core.h process.$(OBJEXT): {$(VPATH)}ruby_assert.h process.$(OBJEXT): {$(VPATH)}ruby_atomic.h process.$(OBJEXT): {$(VPATH)}rubyparser.h @@ -114,6 +114,7 @@ int initgroups(const char *, rb_gid_t); #include "ruby/st.h" #include "ruby/thread.h" #include "ruby/util.h" +#include "ractor_core.h" #include "vm_core.h" #include "vm_sync.h" #include "ruby/ractor.h" @@ -4120,6 +4121,10 @@ rb_fork_async_signal_safe(int *status, rb_pid_t rb_fork_ruby(int *status) { + if (UNLIKELY(!rb_ractor_main_p())) { + rb_raise(rb_eRactorIsolationError, "can not fork from non-main Ractors"); + } + struct rb_process_status child = {.status = 0}; rb_pid_t pid; int try_gc = 1, err = 0; diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb index 3fc891da23..97af7e7413 100644 --- a/test/ruby/test_ractor.rb +++ b/test/ruby/test_ractor.rb @@ -99,6 +99,17 @@ class TestRactor < Test::Unit::TestCase RUBY end + def test_fork_raise_isolation_error + assert_ractor(<<~'RUBY') + ractor = Ractor.new do + Process.fork + rescue Ractor::IsolationError => e + e + end + assert_equal Ractor::IsolationError, ractor.value.class + RUBY + end if Process.respond_to?(:fork) + def test_require_raises_and_no_ractor_belonging_issue assert_ractor(<<~'RUBY') require "tempfile" |