diff options
| author | nagachika <nagachika@ruby-lang.org> | 2024-07-06 13:33:24 +0900 |
|---|---|---|
| committer | nagachika <nagachika@ruby-lang.org> | 2024-07-06 13:35:22 +0900 |
| commit | 89de66dbb0d8454c9d69faa331d6e35f8b315cce (patch) | |
| tree | dbc4da2af6dff635ab3329283a536a7a851b4093 | |
| parent | 5577e5d396cc8f062833b67d6280db6cc8501e7a (diff) | |
merge revision(s) 78d9fe69479d32214a52ad7291c3973f1b6b7f6f, 04729fe68dceddab045be7324e26c2bb15aa62c7: [Backport #20286] [Backport #20286]
Ensure that exiting thread invokes end-of-life behaviour. (#10039)
Fix exception handling in `rb_fiber_scheduler_set`. (#10042)
| -rw-r--r-- | scheduler.c | 18 | ||||
| -rw-r--r-- | test/ruby/test_settracefunc.rb | 48 | ||||
| -rw-r--r-- | thread.c | 33 | ||||
| -rw-r--r-- | version.h | 2 |
4 files changed, 89 insertions, 12 deletions
diff --git a/scheduler.c b/scheduler.c index 477f11c03c..a56549c7d7 100644 --- a/scheduler.c +++ b/scheduler.c @@ -161,6 +161,21 @@ verify_interface(VALUE scheduler) } } +static VALUE +fiber_scheduler_close(VALUE scheduler) +{ + return rb_fiber_scheduler_close(scheduler); +} + +static VALUE +fiber_scheduler_close_ensure(VALUE _thread) +{ + rb_thread_t *thread = (rb_thread_t*)_thread; + thread->scheduler = Qnil; + + return Qnil; +} + VALUE rb_fiber_scheduler_set(VALUE scheduler) { @@ -178,7 +193,8 @@ rb_fiber_scheduler_set(VALUE scheduler) // That way, we do not need to consider interactions, e.g., of a Fiber from // the previous scheduler with the new scheduler. if (thread->scheduler != Qnil) { - rb_fiber_scheduler_close(thread->scheduler); + // rb_fiber_scheduler_close(thread->scheduler); + rb_ensure(fiber_scheduler_close, thread->scheduler, fiber_scheduler_close_ensure, (VALUE)thread); } thread->scheduler = scheduler; diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index d863ebe985..fa1601b0e3 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -2738,4 +2738,52 @@ CODE Foo.foo RUBY 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 @@ -605,14 +605,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); @@ -631,11 +629,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); @@ -687,12 +681,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 4 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 180 +#define RUBY_PATCHLEVEL 181 #include "ruby/version.h" #include "ruby/internal/abi.h" |
