ruby-changes:63028
From: Samuel <ko1@a...>
Date: Mon, 21 Sep 2020 06:51:57 +0900 (JST)
Subject: [ruby-changes:63028] 501fff14c7 (master): When setting current thread scheduler to nil, invoke `#close`.
https://git.ruby-lang.org/ruby.git/commit/?id=501fff14c7 From 501fff14c7657f769d68f90de98fd2ebccb807fb Mon Sep 17 00:00:00 2001 From: Samuel Williams <samuel.williams@o...> Date: Sun, 20 Sep 2020 11:34:02 +1200 Subject: When setting current thread scheduler to nil, invoke `#close`. diff --git a/common.mk b/common.mk index f5ea771..936846d 100644 --- a/common.mk +++ b/common.mk @@ -5214,6 +5214,7 @@ eval.$(OBJEXT): $(top_srcdir)/internal/object.h https://github.com/ruby/ruby/blob/trunk/common.mk#L5214 eval.$(OBJEXT): $(top_srcdir)/internal/serial.h eval.$(OBJEXT): $(top_srcdir)/internal/static_assert.h eval.$(OBJEXT): $(top_srcdir)/internal/string.h +eval.$(OBJEXT): $(top_srcdir)/internal/thread.h eval.$(OBJEXT): $(top_srcdir)/internal/variable.h eval.$(OBJEXT): $(top_srcdir)/internal/vm.h eval.$(OBJEXT): $(top_srcdir)/internal/warnings.h diff --git a/eval.c b/eval.c index 0b51b83..43a5084 100644 --- a/eval.c +++ b/eval.c @@ -28,6 +28,7 @@ https://github.com/ruby/ruby/blob/trunk/eval.c#L28 #include "internal/io.h" #include "internal/mjit.h" #include "internal/object.h" +#include "internal/thread.h" #include "internal/variable.h" #include "iseq.h" #include "mjit.h" @@ -158,6 +159,13 @@ rb_ec_teardown(rb_execution_context_t *ec) https://github.com/ruby/ruby/blob/trunk/eval.c#L159 } static void +rb_ec_scheduler_finalize(rb_execution_context_t *ec) +{ + rb_thread_t *thread = rb_ec_thread_ptr(ec); + rb_thread_scheduler_set(thread->self, Qnil); +} + +static void rb_ec_finalize(rb_execution_context_t *ec) { ruby_sig_finalize(); @@ -270,6 +278,9 @@ rb_ec_cleanup(rb_execution_context_t *ec, volatile int ex) https://github.com/ruby/ruby/blob/trunk/eval.c#L278 } } + // If the user code defined a scheduler for the top level thread, run it: + rb_ec_scheduler_finalize(ec); + mjit_finish(true); // We still need ISeqs here. rb_ec_finalize(ec); diff --git a/internal/scheduler.h b/internal/scheduler.h index 54f59f1..73915ad 100644 --- a/internal/scheduler.h +++ b/internal/scheduler.h @@ -14,6 +14,8 @@ https://github.com/ruby/ruby/blob/trunk/internal/scheduler.h#L14 VALUE rb_scheduler_timeout(struct timeval *timeout); +VALUE rb_scheduler_close(VALUE scheduler); + VALUE rb_scheduler_kernel_sleep(VALUE scheduler, VALUE duration); VALUE rb_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE * argv); diff --git a/scheduler.c b/scheduler.c index f038eed..2dfecaf 100644 --- a/scheduler.c +++ b/scheduler.c @@ -11,9 +11,13 @@ https://github.com/ruby/ruby/blob/trunk/scheduler.c#L11 #include "internal/scheduler.h" #include "ruby/io.h" -static ID id_kernel_sleep; +static ID id_close; + static ID id_block; static ID id_unblock; + +static ID id_kernel_sleep; + static ID id_io_read; static ID id_io_write; static ID id_io_wait; @@ -21,14 +25,23 @@ static ID id_io_wait; https://github.com/ruby/ruby/blob/trunk/scheduler.c#L25 void Init_Scheduler(void) { - id_kernel_sleep = rb_intern_const("kernel_sleep"); + id_close = rb_intern_const("close"); + id_block = rb_intern_const("block"); id_unblock = rb_intern_const("unblock"); + + id_kernel_sleep = rb_intern_const("kernel_sleep"); + id_io_read = rb_intern_const("io_read"); id_io_write = rb_intern_const("io_write"); id_io_wait = rb_intern_const("io_wait"); } +VALUE rb_scheduler_close(VALUE scheduler) +{ + return rb_funcall(scheduler, id_close, 0); +} + VALUE rb_scheduler_timeout(struct timeval *timeout) { if (timeout) { diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb index 43edcb2..10854aa 100644 --- a/test/fiber/scheduler.rb +++ b/test/fiber/scheduler.rb @@ -19,6 +19,8 @@ class Scheduler https://github.com/ruby/ruby/blob/trunk/test/fiber/scheduler.rb#L19 @writable = {} @waiting = {} + @closed = false + @lock = Mutex.new @locking = 0 @ready = [] @@ -96,6 +98,19 @@ class Scheduler https://github.com/ruby/ruby/blob/trunk/test/fiber/scheduler.rb#L98 @urgent = nil end + def close + self.run + ensure + @closed = true + + # We freeze to detect any inadvertant modifications after the scheduler is closed: + self.freeze + end + + def closed? + @closed + end + def current_time Process.clock_gettime(Process::CLOCK_MONOTONIC) end diff --git a/test/fiber/test_scheduler.rb b/test/fiber/test_scheduler.rb index 7acf63d..23fd8b4 100644 --- a/test/fiber/test_scheduler.rb +++ b/test/fiber/test_scheduler.rb @@ -10,4 +10,29 @@ class TestFiberScheduler < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/fiber/test_scheduler.rb#L10 end end end + + def test_closed_at_thread_exit + scheduler = Scheduler.new + + thread = Thread.new do + Thread.current.scheduler = scheduler + end + + thread.join + + assert scheduler.closed? + end + + def test_closed_when_set_to_nil + scheduler = Scheduler.new + + thread = Thread.new do + Thread.current.scheduler = scheduler + Thread.current.scheduler = nil + + assert scheduler.closed? + end + + thread.join + end end diff --git a/thread.c b/thread.c index b3b7a69..53bfbe8 100644 --- a/thread.c +++ b/thread.c @@ -748,10 +748,7 @@ thread_do_start(rb_thread_t *th) https://github.com/ruby/ruby/blob/trunk/thread.c#L748 rb_bug("unreachable"); } - VALUE scheduler = th->scheduler; - if (scheduler != Qnil) { - rb_funcall(scheduler, rb_intern("run"), 0); - } + rb_thread_scheduler_set(th->self, Qnil); } void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec); @@ -3732,6 +3729,11 @@ rb_thread_scheduler_set(VALUE thread, VALUE scheduler) https://github.com/ruby/ruby/blob/trunk/thread.c#L3729 VM_ASSERT(th); + // We invoke Scheduler#close when setting it to something else, to ensure the previous scheduler runs to completion before changing the scheduler. That way, we do not need to consider interactions, e.g., of a Fiber from the previous scheduler with the new scheduler. + if (th->scheduler != Qnil) { + rb_scheduler_close(th->scheduler); + } + th->scheduler = scheduler; return th->scheduler; -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/