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
[Bug #16664]

(cherry picked from commit adcf0316d1)
This commit is contained in:
Takashi Kokubun 2020-02-28 23:58:33 -08:00 committed by NARUSE, Yui
parent 17e925b991
commit a36ead3f4f
4 changed files with 50 additions and 8 deletions

21
cont.c
View file

@ -1096,6 +1096,15 @@ cont_save_thread(rb_context_t *cont, rb_thread_t *th)
sec->machine.stack_end = NULL; 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 static void
cont_init(rb_context_t *cont, rb_thread_t *th) cont_init(rb_context_t *cont, rb_thread_t *th)
{ {
@ -1105,9 +1114,7 @@ cont_init(rb_context_t *cont, rb_thread_t *th)
cont->saved_ec.local_storage = NULL; cont->saved_ec.local_storage = NULL;
cont->saved_ec.local_storage_recursive_hash = Qnil; cont->saved_ec.local_storage_recursive_hash = Qnil;
cont->saved_ec.local_storage_recursive_hash_for_trace = Qnil; cont->saved_ec.local_storage_recursive_hash_for_trace = Qnil;
if (mjit_enabled) { cont_init_mjit_cont(cont);
cont->mjit_cont = mjit_cont_new(&cont->saved_ec);
}
} }
static rb_context_t * static rb_context_t *
@ -1124,6 +1131,14 @@ cont_new(VALUE klass)
return cont; 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 #if 0
void void
show_vm_stack(const rb_execution_context_t *ec) show_vm_stack(const rb_execution_context_t *ec)

View file

@ -1505,9 +1505,11 @@ VALUE rb_dbl_complex_new_polar_pi(double abs, double ang);
struct rb_thread_struct; struct rb_thread_struct;
/* cont.c */ /* cont.c */
struct rb_fiber_struct;
VALUE rb_obj_is_fiber(VALUE); VALUE rb_obj_is_fiber(VALUE);
void rb_fiber_reset_root_local_storage(struct rb_thread_struct *); 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 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);
/* debug.c */ /* debug.c */
PRINTF_ARGS(void ruby_debug_printf(const char*, ...), 1, 2); PRINTF_ARGS(void ruby_debug_printf(const char*, ...), 1, 2);

3
mjit.c
View file

@ -801,6 +801,9 @@ mjit_init(const struct mjit_options *opts)
rb_native_cond_initialize(&mjit_worker_wakeup); rb_native_cond_initialize(&mjit_worker_wakeup);
rb_native_cond_initialize(&mjit_gc_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 // Initialize class_serials cache for compilation
valid_class_serials = rb_hash_new(); valid_class_serials = rb_hash_new();
rb_obj_hide(valid_class_serials); rb_obj_hide(valid_class_serials);

View file

@ -12,6 +12,10 @@ class TestJIT < Test::Unit::TestCase
/\AJIT inline: .+\n\z/, /\AJIT inline: .+\n\z/,
/\ASuccessful MJIT finish\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... # trace_* insns are not compiled for now...
TEST_PENDING_INSNS = RubyVM::INSTRUCTION_NAMES.select { |n| n.start_with?('trace_') }.map(&:to_sym) + [ 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 end
def test_nothing_to_unload_with_jit_wait def test_nothing_to_unload_with_jit_wait
ignorable_patterns = [ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 11, max_cache: 10, ignorable_patterns: MAX_CACHE_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)
begin; begin;
def a1() a2() end def a1() a2() end
def a2() a3() end def a2() a3() end
@ -632,6 +632,28 @@ class TestJIT < Test::Unit::TestCase
end; end;
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 def test_unload_units_and_compaction
Dir.mktmpdir("jit_test_unload_units_") do |dir| Dir.mktmpdir("jit_test_unload_units_") do |dir|
# MIN_CACHE_SIZE is 10 # MIN_CACHE_SIZE is 10