mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	Add hook for Timeout.timeout.
				
					
				
			This commit is contained in:
		
							parent
							
								
									93753d7ee7
								
							
						
					
					
						commit
						4c53dc970b
					
				
				
				Notes:
				
					git
				
				2021-03-30 14:39:05 +09:00 
				
			
			
			
		
		
					 5 changed files with 71 additions and 5 deletions
				
			
		| 
						 | 
				
			
			@ -22,6 +22,8 @@ VALUE rb_fiber_scheduler_make_timeout(struct timeval *timeout);
 | 
			
		|||
 | 
			
		||||
VALUE rb_fiber_scheduler_close(VALUE scheduler);
 | 
			
		||||
 | 
			
		||||
VALUE rb_fiber_scheduler_timeout_raise(VALUE scheduler, VALUE duration);
 | 
			
		||||
 | 
			
		||||
VALUE rb_fiber_scheduler_kernel_sleep(VALUE scheduler, VALUE duration);
 | 
			
		||||
VALUE rb_fiber_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE * argv);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -76,9 +76,15 @@ module Timeout
 | 
			
		|||
  # Note that this is both a method of module Timeout, so you can <tt>include
 | 
			
		||||
  # Timeout</tt> into your classes so they have a #timeout method, as well as
 | 
			
		||||
  # a module method, so you can call it directly as Timeout.timeout().
 | 
			
		||||
  def timeout(sec, klass = nil, message = nil)   #:yield: +sec+
 | 
			
		||||
  def timeout(sec, klass = nil, message = nil, &block)   #:yield: +sec+
 | 
			
		||||
    return yield(sec) if sec == nil or sec.zero?
 | 
			
		||||
 | 
			
		||||
    message ||= "execution expired".freeze
 | 
			
		||||
 | 
			
		||||
    if scheduler = Fiber.scheduler and scheduler.respond_to?(:timeout_raise)
 | 
			
		||||
      return scheduler.timeout_raise(sec, klass || Error, message, &block)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    from = "from #{caller_locations(1, 1)[0]}" if $DEBUG
 | 
			
		||||
    e = Error
 | 
			
		||||
    bl = proc do |exception|
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@ static ID id_close;
 | 
			
		|||
static ID id_block;
 | 
			
		||||
static ID id_unblock;
 | 
			
		||||
 | 
			
		||||
static ID id_timeout_raise;
 | 
			
		||||
static ID id_kernel_sleep;
 | 
			
		||||
static ID id_process_wait;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +33,7 @@ Init_Fiber_Scheduler(void)
 | 
			
		|||
    id_block = rb_intern_const("block");
 | 
			
		||||
    id_unblock = rb_intern_const("unblock");
 | 
			
		||||
 | 
			
		||||
    id_timeout_raise = rb_intern_const("timeout_raise");
 | 
			
		||||
    id_kernel_sleep = rb_intern_const("kernel_sleep");
 | 
			
		||||
    id_process_wait = rb_intern_const("process_wait");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -108,6 +110,12 @@ rb_fiber_scheduler_make_timeout(struct timeval *timeout)
 | 
			
		|||
    return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VALUE
 | 
			
		||||
rb_fiber_scheduler_timeout_raise(VALUE scheduler, VALUE timeout)
 | 
			
		||||
{
 | 
			
		||||
    return rb_funcall(scheduler, id_timeout_raise, 1, timeout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VALUE
 | 
			
		||||
rb_fiber_scheduler_kernel_sleep(VALUE scheduler, VALUE timeout)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,10 +81,12 @@ class Scheduler
 | 
			
		|||
        waiting, @waiting = @waiting, {}
 | 
			
		||||
 | 
			
		||||
        waiting.each do |fiber, timeout|
 | 
			
		||||
          if timeout <= time
 | 
			
		||||
            fiber.resume
 | 
			
		||||
          else
 | 
			
		||||
            @waiting[fiber] = timeout
 | 
			
		||||
          if fiber.alive?
 | 
			
		||||
            if timeout <= time
 | 
			
		||||
              fiber.resume
 | 
			
		||||
            else
 | 
			
		||||
              @waiting[fiber] = timeout
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
| 
						 | 
				
			
			@ -127,6 +129,24 @@ class Scheduler
 | 
			
		|||
    Process.clock_gettime(Process::CLOCK_MONOTONIC)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def timeout_raise(duration, klass, message, &block)
 | 
			
		||||
    fiber = Fiber.current
 | 
			
		||||
 | 
			
		||||
    self.fiber do
 | 
			
		||||
      sleep(duration)
 | 
			
		||||
 | 
			
		||||
      if fiber&.alive?
 | 
			
		||||
        fiber.raise(klass, message)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    begin
 | 
			
		||||
      yield(duration)
 | 
			
		||||
    ensure
 | 
			
		||||
      fiber = nil
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def process_wait(pid, flags)
 | 
			
		||||
    # $stderr.puts [__method__, pid, flags, Fiber.current].inspect
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										30
									
								
								test/fiber/test_timeout.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								test/fiber/test_timeout.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
# frozen_string_literal: true
 | 
			
		||||
require 'test/unit'
 | 
			
		||||
require_relative 'scheduler'
 | 
			
		||||
 | 
			
		||||
require 'timeout'
 | 
			
		||||
 | 
			
		||||
class TestFiberTimeout < Test::Unit::TestCase
 | 
			
		||||
  def test_timeout_raise
 | 
			
		||||
    error = nil
 | 
			
		||||
 | 
			
		||||
    thread = Thread.new do
 | 
			
		||||
      scheduler = Scheduler.new
 | 
			
		||||
      Fiber.set_scheduler scheduler
 | 
			
		||||
 | 
			
		||||
      Fiber.schedule do
 | 
			
		||||
        begin
 | 
			
		||||
          Timeout.timeout(0.01) do
 | 
			
		||||
            sleep(1)
 | 
			
		||||
          end
 | 
			
		||||
        rescue
 | 
			
		||||
          error = $!
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    thread.join
 | 
			
		||||
 | 
			
		||||
    assert_kind_of(Timeout::Error, error)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue