From 217a82fdb6db88dc4f06995b6981b80526d03e15 Mon Sep 17 00:00:00 2001 From: Matthew Gaudet Date: Wed, 19 Apr 2017 14:08:10 -0400 Subject: [PATCH] Add an async thread to the ruby interpreter. A problem I see, is if unblock_compilation_thread gets called, the interpreter hangs on shutdown. Reproduce the issue by building ruby and running repro.sh. Note that if you remove RELEASE_GVL from repro.sh, you won't see the error. Signed-off-by: Matthew Gaudet --- eval.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ repro.sh | 1 + test.rb | 2 ++ 3 files changed, 52 insertions(+) create mode 100644 repro.sh create mode 100644 test.rb diff --git a/eval.c b/eval.c index 8c22b62..a37118b 100644 --- a/eval.c +++ b/eval.c @@ -18,6 +18,7 @@ #include "ruby/vm.h" #include "vm_core.h" #include "probes_helper.h" +#include "ruby/thread.h" NORETURN(void rb_raise_jump(VALUE, VALUE)); @@ -230,6 +231,51 @@ ruby_cleanup(volatile int ex) return sysex; } +#define async_trace(...) if (getenv("ASYNC_COMPILATION_TRACE")) { fprintf(stderr, __VA_ARGS__); } + +static int compilation_thread_started = 0; +void unblock_compilation_thread(void* arg) { + async_trace("Unblock called!, arg address is %p", arg); + *(int*)arg = 0; // interrupt compilation thread. +} + +void* vm_compile_thread(void *vm) { + async_trace("invoked compile thread"); + while (compilation_thread_started) { + rb_thread_wait_for(rb_time_interval(DBL2NUM(0.01)));; + } + async_trace("compilation thread stopped. Returning NULL"); + return NULL; +} +/** + * Release the GVL then start compilation thread. + */ +VALUE releaseGVLandStartCompilationThread(rb_vm_t* vm) + { + async_trace( "inside %s, compilationThread address is %p\n",__FUNCTION__, &compilation_thread_started); + compilation_thread_started = 1; + if (getenv("RELEASE_GVL")) + rb_thread_call_without_gvl2(vm_compile_thread, /* func */ + (void*)vm, /* func arg */ + unblock_compilation_thread, /* unblock func */ + &compilation_thread_started); /* unblock arg */ + else + vm_compile_thread(vm); + + async_trace( "inside %s, rb_thread_call_without_gvl has returned. Returning Qnil\n",__FUNCTION__); + return Qnil; + } + +void +kickoff_thread(rb_vm_t* vm) +{ + typedef VALUE (*thread_function)(ANYARGS); + async_trace("calling thread create"); + rb_thread_create((thread_function)(releaseGVLandStartCompilationThread),vm); + async_trace("Thread create returned"); + +} + static int ruby_exec_internal(void *n) { @@ -239,6 +285,9 @@ ruby_exec_internal(void *n) if (!n) return 0; + if (getenv("ASYNC_COMPILATION_TRACE")) + kickoff_thread(GET_VM()); + TH_PUSH_TAG(th); if ((state = EXEC_TAG()) == 0) { SAVE_ROOT_JMPBUF(th, { diff --git a/repro.sh b/repro.sh new file mode 100644 index 0000000..4f43e72 --- /dev/null +++ b/repro.sh @@ -0,0 +1 @@ +for i in `seq 1 10`; do RELEASE_GVL=1 ASYNC_COMPILATION_TRACE=1 ./miniruby test.rb -zzz=678 || exit; done; diff --git a/test.rb b/test.rb new file mode 100644 index 0000000..83b0640 --- /dev/null +++ b/test.rb @@ -0,0 +1,2 @@ +#! /usr/local/bin/ruby -s +print "#{$zzz}\n" -- 2.7.4