diff options
| -rw-r--r-- | test/ruby/test_settracefunc.rb | 48 | ||||
| -rw-r--r-- | thread.c | 33 | ||||
| -rw-r--r-- | version.h | 2 |
3 files changed, 72 insertions, 11 deletions
diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index dbaf0aaf09..a90c885247 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -2874,4 +2874,52 @@ CODE assert err.kind_of?(RuntimeError) assert_equal err.message.to_i + 3, line end + + def test_tracepoint_thread_begin + target_thread = nil + + trace = TracePoint.new(:thread_begin) do |tp| + target_thread = tp.self + end + + trace.enable(target_thread: nil) do + Thread.new{}.join + end + + assert_kind_of(Thread, target_thread) + end + + def test_tracepoint_thread_end + target_thread = nil + + trace = TracePoint.new(:thread_end) do |tp| + target_thread = tp.self + end + + trace.enable(target_thread: nil) do + Thread.new{}.join + end + + assert_kind_of(Thread, target_thread) + end + + def test_tracepoint_thread_end_with_exception + target_thread = nil + + trace = TracePoint.new(:thread_end) do |tp| + target_thread = tp.self + end + + trace.enable(target_thread: nil) do + thread = Thread.new do + Thread.current.report_on_exception = false + raise + end + + # Ignore the exception raised by the thread: + thread.join rescue nil + end + + assert_kind_of(Thread, target_thread) + end end @@ -602,14 +602,12 @@ thread_do_start_proc(rb_thread_t *th) } } -static void +static VALUE thread_do_start(rb_thread_t *th) { native_set_thread_name(th); VALUE result = Qundef; - EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef); - switch (th->invoke_type) { case thread_invoke_type_proc: result = thread_do_start_proc(th); @@ -628,11 +626,7 @@ thread_do_start(rb_thread_t *th) rb_bug("unreachable"); } - rb_fiber_scheduler_set(Qnil); - - th->value = result; - - EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef); + return result; } void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec); @@ -665,12 +659,31 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start) // Ensure that we are not joinable. VM_ASSERT(UNDEF_P(th->value)); + int fiber_scheduler_closed = 0, event_thread_end_hooked = 0; + VALUE result = Qundef; + EC_PUSH_TAG(th->ec); if ((state = EC_EXEC_TAG()) == TAG_NONE) { - SAVE_ROOT_JMPBUF(th, thread_do_start(th)); + EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef); + + SAVE_ROOT_JMPBUF(th, result = thread_do_start(th)); } - else { + + if (!fiber_scheduler_closed) { + fiber_scheduler_closed = 1; + rb_fiber_scheduler_set(Qnil); + } + + if (!event_thread_end_hooked) { + event_thread_end_hooked = 1; + EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef); + } + + if (state == TAG_NONE) { + // This must be set AFTER doing all user-level code. At this point, the thread is effectively finished and calls to `Thread#join` will succeed. + th->value = result; + } else { errinfo = th->ec->errinfo; VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef); @@ -11,7 +11,7 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 1 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 60 +#define RUBY_PATCHLEVEL 61 #include "ruby/version.h" #include "ruby/internal/abi.h" |
