mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	When setting current thread scheduler to nil, invoke #close.
				
					
				
			This commit is contained in:
		
							parent
							
								
									b6d599d76e
								
							
						
					
					
						commit
						501fff14c7
					
				
				
				Notes:
				
					git
				
				2020-09-21 06:52:08 +09:00 
				
			
			
			
		
		
					 7 changed files with 75 additions and 6 deletions
				
			
		|  | @ -5214,6 +5214,7 @@ eval.$(OBJEXT): $(top_srcdir)/internal/object.h | |||
| 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 | ||||
|  |  | |||
							
								
								
									
										11
									
								
								eval.c
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								eval.c
									
										
									
									
									
								
							|  | @ -28,6 +28,7 @@ | |||
| #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" | ||||
|  | @ -157,6 +158,13 @@ rb_ec_teardown(rb_execution_context_t *ec) | |||
|     rb_ec_clear_all_trace_func(ec); | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| { | ||||
|  | @ -270,6 +278,9 @@ rb_ec_cleanup(rb_execution_context_t *ec, volatile int ex) | |||
| 	} | ||||
|     } | ||||
| 
 | ||||
|     // 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); | ||||
|  |  | |||
|  | @ -14,6 +14,8 @@ | |||
| 
 | ||||
| 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); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										17
									
								
								scheduler.c
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								scheduler.c
									
										
									
									
									
								
							|  | @ -11,9 +11,13 @@ | |||
| #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; | |||
| 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) { | ||||
|  |  | |||
|  | @ -19,6 +19,8 @@ class Scheduler | |||
|     @writable = {} | ||||
|     @waiting = {} | ||||
| 
 | ||||
|     @closed = false | ||||
| 
 | ||||
|     @lock = Mutex.new | ||||
|     @locking = 0 | ||||
|     @ready = [] | ||||
|  | @ -96,6 +98,19 @@ class Scheduler | |||
|     @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 | ||||
|  |  | |||
|  | @ -10,4 +10,29 @@ class TestFiberScheduler < Test::Unit::TestCase | |||
|       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 | ||||
|  |  | |||
							
								
								
									
										10
									
								
								thread.c
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								thread.c
									
										
									
									
									
								
							|  | @ -748,10 +748,7 @@ thread_do_start(rb_thread_t *th) | |||
|         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) | |||
| 
 | ||||
|     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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Samuel Williams
						Samuel Williams