From ffd04314c62836b439954a373e2e76e7965765dd Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Thu, 21 Jan 2021 14:29:29 -0800 Subject: [PATCH] Use main Ractor EC on threads without EC Previously in threads which weren't created to running Ruby (which includes the timer thread), calling rb_postponed_job_register_one as well as probably similar hooks would cause a segfault due to the now-thread-local EC being NULL. This was particularly a problem for profiling tools (like stackprof), which rely on signal handlers, which may be sent to any thread in the process including those not managed by Ruby. [Fixes #17573] Co-authored-by: John Crepezzi --- ext/-test-/postponed_job/postponed_job.c | 31 +++++++++++++++++++ ractor.c | 2 +- .../-ext-/postponed_job/test_postponed_job.rb | 7 +++++ vm_core.h | 7 +++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/ext/-test-/postponed_job/postponed_job.c b/ext/-test-/postponed_job/postponed_job.c index d8684d475a45..96f4562b61f6 100644 --- a/ext/-test-/postponed_job/postponed_job.c +++ b/ext/-test-/postponed_job/postponed_job.c @@ -58,6 +58,34 @@ pjob_call_direct(VALUE self, VALUE obj) return self; } +#ifdef HAVE_PTHREAD_H +#include + +static void * +pjob_register_in_c_thread_i(void *obj) +{ + rb_postponed_job_register_one(0, pjob_one_callback, (void *)obj); + rb_postponed_job_register_one(0, pjob_one_callback, (void *)obj); + rb_postponed_job_register_one(0, pjob_one_callback, (void *)obj); + return NULL; +} + +static VALUE +pjob_register_in_c_thread(VALUE self, VALUE obj) +{ + pthread_t thread; + if(pthread_create(&thread, NULL, pjob_register_in_c_thread_i, (void *)obj)) { + return Qfalse; + } + + if(pthread_join(thread, NULL)) { + return Qfalse; + } + + return Qtrue; +} +#endif + void Init_postponed_job(VALUE self) { @@ -65,5 +93,8 @@ Init_postponed_job(VALUE self) rb_define_module_function(mBug, "postponed_job_register", pjob_register, 1); rb_define_module_function(mBug, "postponed_job_register_one", pjob_register_one, 1); rb_define_module_function(mBug, "postponed_job_call_direct", pjob_call_direct, 1); +#ifdef HAVE_PTHREAD_H + rb_define_module_function(mBug, "postponed_job_register_in_c_thread", pjob_register_in_c_thread, 1); +#endif } diff --git a/ractor.c b/ractor.c index c5bb0af5a4d8..d3f3ff1bfb20 100644 --- a/ractor.c +++ b/ractor.c @@ -1979,7 +1979,7 @@ rb_ractor_terminate_all(void) RB_VM_UNLOCK(); } -rb_execution_context_t * +MJIT_FUNC_EXPORTED rb_execution_context_t * rb_vm_main_ractor_ec(rb_vm_t *vm) { return vm->ractor.main_ractor->threads.running_ec; diff --git a/test/-ext-/postponed_job/test_postponed_job.rb b/test/-ext-/postponed_job/test_postponed_job.rb index 7dc28776d0c5..fee0172d114f 100644 --- a/test/-ext-/postponed_job/test_postponed_job.rb +++ b/test/-ext-/postponed_job/test_postponed_job.rb @@ -25,4 +25,11 @@ def test_register Bug.postponed_job_register_one(ary = []) assert_equal [1], ary end + + if Bug.respond_to?(:postponed_job_register_in_c_thread) + def test_register_in_c_thread + assert Bug.postponed_job_register_in_c_thread(ary = []) + assert_equal [1], ary + end + end end diff --git a/vm_core.h b/vm_core.h index 5f8d4ab87670..b67e5de8835e 100644 --- a/vm_core.h +++ b/vm_core.h @@ -1788,6 +1788,8 @@ rb_ec_vm_ptr(const rb_execution_context_t *ec) } } +static inline rb_vm_t * rb_current_vm(void); + static inline rb_execution_context_t * rb_current_execution_context(void) { @@ -1800,6 +1802,11 @@ rb_current_execution_context(void) #else rb_execution_context_t *ec = native_tls_get(ruby_current_ec_key); #endif + + if (ec == NULL) { + ec = rb_vm_main_ractor_ec(GET_VM()); + } + VM_ASSERT(ec != NULL); return ec; }