summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornagachika <nagachika@ruby-lang.org>2024-07-06 13:33:24 +0900
committernagachika <nagachika@ruby-lang.org>2024-07-06 13:35:22 +0900
commit89de66dbb0d8454c9d69faa331d6e35f8b315cce (patch)
treedbc4da2af6dff635ab3329283a536a7a851b4093
parent5577e5d396cc8f062833b67d6280db6cc8501e7a (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.c18
-rw-r--r--test/ruby/test_settracefunc.rb48
-rw-r--r--thread.c33
-rw-r--r--version.h2
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
diff --git a/thread.c b/thread.c
index 8309940999..7c5183da56 100644
--- a/thread.c
+++ b/thread.c
@@ -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);
diff --git a/version.h b/version.h
index e294d76a60..d36bdd23f5 100644
--- a/version.h
+++ b/version.h
@@ -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"