From adcf0316d1ecedae2a9157ad941550e0c0fb510b Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 28 Feb 2020 23:58:33 -0800 Subject: Prevent unloading methods used in root_fiber while calling another Fiber (#2939) Fixing SEGVs like: http://ci.rvm.jp/results/trunk-mjit-wait@silicon-docker/2744905 http://ci.rvm.jp/results/trunk-mjit-wait@silicon-docker/2744420 http://ci.rvm.jp/results/trunk-mjit-wait@silicon-docker/2741400 --- common.mk | 1 + cont.c | 21 ++++++++++++++++++--- internal/cont.h | 2 ++ mjit.c | 4 ++++ test/ruby/test_jit.rb | 32 +++++++++++++++++++++++++++----- 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/common.mk b/common.mk index 57e11abb48..b2cb7dad7c 100644 --- a/common.mk +++ b/common.mk @@ -2907,6 +2907,7 @@ mjit.$(OBJEXT): $(hdrdir)/ruby/ruby.h mjit.$(OBJEXT): $(top_srcdir)/internal/array.h mjit.$(OBJEXT): $(top_srcdir)/internal/class.h mjit.$(OBJEXT): $(top_srcdir)/internal/compilers.h +mjit.$(OBJEXT): $(top_srcdir)/internal/cont.h mjit.$(OBJEXT): $(top_srcdir)/internal/file.h mjit.$(OBJEXT): $(top_srcdir)/internal/gc.h mjit.$(OBJEXT): $(top_srcdir)/internal/hash.h diff --git a/cont.c b/cont.c index df197a7363..7a32573942 100644 --- a/cont.c +++ b/cont.c @@ -1103,6 +1103,15 @@ cont_save_thread(rb_context_t *cont, rb_thread_t *th) sec->machine.stack_end = NULL; } +static void +cont_init_mjit_cont(rb_context_t *cont) +{ + VM_ASSERT(cont->mjit_cont == NULL); + if (mjit_enabled) { + cont->mjit_cont = mjit_cont_new(&(cont->saved_ec)); + } +} + static void cont_init(rb_context_t *cont, rb_thread_t *th) { @@ -1112,9 +1121,7 @@ cont_init(rb_context_t *cont, rb_thread_t *th) cont->saved_ec.local_storage = NULL; cont->saved_ec.local_storage_recursive_hash = Qnil; cont->saved_ec.local_storage_recursive_hash_for_trace = Qnil; - if (mjit_enabled) { - cont->mjit_cont = mjit_cont_new(&cont->saved_ec); - } + cont_init_mjit_cont(cont); } static rb_context_t * @@ -1131,6 +1138,14 @@ cont_new(VALUE klass) return cont; } +void +rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber) +{ + // Currently this function is meant for root_fiber. Others go through cont_new. + // XXX: Is this mjit_cont `mjit_cont_free`d? + cont_init_mjit_cont(&fiber->cont); +} + #if 0 void show_vm_stack(const rb_execution_context_t *ec) diff --git a/internal/cont.h b/internal/cont.h index 5aaf095142..f77fb20a5c 100644 --- a/internal/cont.h +++ b/internal/cont.h @@ -12,10 +12,12 @@ #include "ruby/ruby.h" /* for VALUE */ struct rb_thread_struct; /* in vm_core.h */ +struct rb_fiber_struct; /* in cont.c */ /* cont.c */ VALUE rb_obj_is_fiber(VALUE); void rb_fiber_reset_root_local_storage(struct rb_thread_struct *); void ruby_register_rollback_func_for_ensure(VALUE (*ensure_func)(VALUE), VALUE (*rollback_func)(VALUE)); +void rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber); #endif /* INTERNAL_CONT_H */ diff --git a/mjit.c b/mjit.c index b5fcffab10..a5461414d3 100644 --- a/mjit.c +++ b/mjit.c @@ -19,6 +19,7 @@ #include "id_table.h" #include "internal.h" #include "internal/class.h" +#include "internal/cont.h" #include "internal/file.h" #include "internal/hash.h" #include "internal/mjit.h" @@ -811,6 +812,9 @@ mjit_init(const struct mjit_options *opts) rb_native_cond_initialize(&mjit_worker_wakeup); rb_native_cond_initialize(&mjit_gc_wakeup); + // Make sure root_fiber's saved_ec is scanned by mark_ec_units + rb_fiber_init_mjit_cont(GET_EC()->fiber_ptr); + // Initialize class_serials cache for compilation valid_class_serials = rb_hash_new(); rb_obj_hide(valid_class_serials); diff --git a/test/ruby/test_jit.rb b/test/ruby/test_jit.rb index adb66036de..e3d8f9cee2 100644 --- a/test/ruby/test_jit.rb +++ b/test/ruby/test_jit.rb @@ -12,6 +12,10 @@ class TestJIT < Test::Unit::TestCase /\AJIT inline: .+\n\z/, /\ASuccessful MJIT finish\n\z/, ] + MAX_CACHE_PATTERNS = [ + /\AJIT compaction \([^)]+\): .+\n\z/, + /\ANo units can be unloaded -- .+\n\z/, + ] # trace_* insns are not compiled for now... TEST_PENDING_INSNS = RubyVM::INSTRUCTION_NAMES.select { |n| n.start_with?('trace_') }.map(&:to_sym) + [ @@ -611,11 +615,7 @@ class TestJIT < Test::Unit::TestCase end def test_nothing_to_unload_with_jit_wait - ignorable_patterns = [ - /\AJIT compaction \([^)]+\): .+\n\z/, - /\ANo units can be unloaded -- .+\n\z/, - ] - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 11, max_cache: 10, ignorable_patterns: ignorable_patterns) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 11, max_cache: 10, ignorable_patterns: MAX_CACHE_PATTERNS) begin; def a1() a2() end def a2() a3() end @@ -632,6 +632,28 @@ class TestJIT < Test::Unit::TestCase end; end + def test_unload_units_on_fiber + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 12, max_cache: 10, ignorable_patterns: MAX_CACHE_PATTERNS) + begin; + def a1() a2(false); a2(true) end + def a2(a) a3(a) end + def a3(a) a4(a) end + def a4(a) a5(a) end + def a5(a) a6(a) end + def a6(a) a7(a) end + def a7(a) a8(a) end + def a8(a) a9(a) end + def a9(a) a10(a) end + def a10(a) + if a + Fiber.new { a11 }.resume + end + end + def a11() print('hello') end + a1 + end; + end + def test_unload_units_and_compaction Dir.mktmpdir("jit_test_unload_units_") do |dir| # MIN_CACHE_SIZE is 10 -- cgit v1.2.3