[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]