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

ruby-changes:26159

From: kosaki <ko1@a...>
Date: Thu, 6 Dec 2012 00:04:21 +0900 (JST)
Subject: [ruby-changes:26159] kosaki:r38216 (trunk): * lib/timeout.rb (Timeout#timeout): set

kosaki	2012-12-06 00:04:09 +0900 (Thu, 06 Dec 2012)

  New Revision: 38216

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=38216

  Log:
    * lib/timeout.rb (Timeout#timeout): set
      async_interrupt_timeing(:on_blocking) by default.
      [Bug #7503] [ruby-core:50524]
    
    * test/test_timeout.rb (#test_timeout_blocking): test for the above.
    * test/test_timeout.rb (test_timeout_immediate): ditto
    * test/test_timeout.rb (test_timeout_immediate2): ditto.
    
    * NEWS: news for the above.

  Modified files:
    trunk/ChangeLog
    trunk/NEWS
    trunk/lib/timeout.rb
    trunk/test/test_timeout.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 38215)
+++ ChangeLog	(revision 38216)
@@ -1,3 +1,14 @@
+Wed Dec  5 04:50:17 2012  KOSAKI Motohiro  <kosaki.motohiro@g...>
+	* lib/timeout.rb (Timeout#timeout): set
+	  async_interrupt_timeing(:on_blocking) by default.
+	  [Bug #7503] [ruby-core:50524]
+
+	* test/test_timeout.rb (#test_timeout_blocking): test for the above.
+	* test/test_timeout.rb (test_timeout_immediate): ditto
+	* test/test_timeout.rb (test_timeout_immediate2): ditto.
+
+	* NEWS: news for the above.
+
 Wed Dec  5 23:50:23 2012  Narihiro Nakamura  <authornari@g...>
 
 	* gc.c (getrusage_time): uses clock_gettime() with
Index: lib/timeout.rb
===================================================================
--- lib/timeout.rb	(revision 38215)
+++ lib/timeout.rb	(revision 38216)
@@ -47,39 +47,41 @@
   # 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)   #:yield: +sec+
-    return yield(sec) if sec == nil or sec.zero?
-    exception = klass || Class.new(ExitException)
-    begin
+  def timeout(sec, klass = nil, immediate: false)   #:yield: +sec+
+      return yield(sec) if sec == nil or sec.zero?
+    Thread.async_interrupt_timing(klass ? klass : ExitException => immediate ? :immediate : :on_blocking) do
+      exception = klass || Class.new(ExitException)
       begin
-        x = Thread.current
-        y = Thread.start {
-          begin
-            sleep sec
-          rescue => e
-            x.raise e
-          else
-            x.raise exception, "execution expired"
+        begin
+          x = Thread.current
+          y = Thread.start {
+            begin
+              sleep sec
+            rescue => e
+              x.raise e
+            else
+              x.raise exception, "execution expired"
+            end
+          }
+          return yield(sec)
+        ensure
+          if y
+            y.kill
+            y.join # make sure y is dead.
           end
-        }
-        return yield(sec)
-      ensure
-        if y
-          y.kill
-          y.join # make sure y is dead.
         end
+      rescue exception => e
+        rej = /\A#{Regexp.quote(__FILE__)}:#{__LINE__-4}\z/o
+        (bt = e.backtrace).reject! {|m| rej =~ m}
+        level = -caller(CALLER_OFFSET).size
+        while THIS_FILE =~ bt[level]
+          bt.delete_at(level)
+          level += 1
+        end
+        raise if klass            # if exception class is specified, it
+                                  # would be expected outside.
+        raise Error, e.message, e.backtrace
       end
-    rescue exception => e
-      rej = /\A#{Regexp.quote(__FILE__)}:#{__LINE__-4}\z/o
-      (bt = e.backtrace).reject! {|m| rej =~ m}
-      level = -caller(CALLER_OFFSET).size
-      while THIS_FILE =~ bt[level]
-        bt.delete_at(level)
-        level += 1
-      end
-      raise if klass            # if exception class is specified, it
-                                # would be expected outside.
-      raise Error, e.message, e.backtrace
     end
   end
 
Index: NEWS
===================================================================
--- NEWS	(revision 38215)
+++ NEWS	(revision 38216)
@@ -332,6 +332,13 @@
     are introduced for easy detection of available constants on a
     running system.
 
+* timeout
+  * Timeout.timeout supports immediate optional keyword parameter.
+  * incompatible changes:
+    * Timeout.timeout now use async_interrupt_timing(:on_blocking) by default.
+      And then, timeout is only happen on blocking point (e.g. sleep, read,
+       write, Mutex#lock and so on).
+
 * tmpdir
   * incompatible changes:
     * Dir.mktmpdir uses FileUtils.remove_entry instead of
Index: test/test_timeout.rb
===================================================================
--- test/test_timeout.rb	(revision 38215)
+++ test/test_timeout.rb	(revision 38216)
@@ -18,7 +18,7 @@
     }
     assert_nothing_raised("[ruby-dev:38319]") do
       Timeout.timeout(1) {
-        nil while @flag
+        sleep 0.01 while @flag
       }
     end
     assert !@flag, "[ruby-dev:38319]"
@@ -29,4 +29,65 @@
     def (n = Object.new).zero?; false; end
     assert_raise(TypeError, bug3168) {Timeout.timeout(n) { sleep 0.1 }}
   end
+
+  def test_timeout_immediate
+    begin
+      t = Thread.new {
+        Timeout.timeout(0.1, immediate: true) {
+          # loop forever, but can be interrupted
+          loop {}
+        }
+      }
+      sleep 0.5
+      t.raise RuntimeError
+      assert_raise(Timeout::Error) {
+        t.join
+      }
+    ensure
+      t.kill if t.alive?
+      begin
+        t.join
+      rescue Exception
+      end
+    end
+  end
+
+  def test_timeout_immediate2
+    begin
+      t = Thread.new {
+        Timeout.timeout(0.1) {
+          # loop forever, must not interrupted
+          loop {}
+        }
+      }
+      sleep 0.5
+      t.raise RuntimeError
+      assert_raise(RuntimeError) {
+        t.join
+      }
+    ensure
+      t.kill if t.alive?
+      begin
+        t.join
+      rescue Exception
+      end
+    end
+  end
+
+  def test_timeout_blocking
+    t0 = Time.now
+    begin
+      Timeout.timeout(0.1) {
+        while true do
+          t1 = Time.now
+          break if t1 - t0 > 1
+        end
+        sleep 2
+      }
+    rescue Timeout::Error
+    end
+    t1 = Time.now
+    assert (t1 - t0) >= 1
+    assert (t1 - t0) < 2
+  end
 end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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