From a400c94d7272d29f09b16c059bb9ca3b34bd398f Mon Sep 17 00:00:00 2001 From: kosaki Date: Wed, 5 Dec 2012 15:04:09 +0000 Subject: * 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38216 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 11 +++++++++ NEWS | 7 ++++++ lib/timeout.rb | 60 +++++++++++++++++++++++++------------------------ test/test_timeout.rb | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 111 insertions(+), 30 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0241436afb..7969290500 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Wed Dec 5 04:50:17 2012 KOSAKI Motohiro + * 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 * gc.c (getrusage_time): uses clock_gettime() with diff --git a/NEWS b/NEWS index db31e6773f..144b3f91ba 100644 --- a/NEWS +++ b/NEWS @@ -332,6 +332,13 @@ with all sufficient information, see the ChangeLog file. 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 diff --git a/lib/timeout.rb b/lib/timeout.rb index 7fd87ff40b..12c110647e 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -47,39 +47,41 @@ module Timeout # Note that this is both a method of module Timeout, so you can include # Timeout 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 diff --git a/test/test_timeout.rb b/test/test_timeout.rb index 57eca3e478..dcd6206737 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -18,7 +18,7 @@ class TestTimeout < Test::Unit::TestCase } 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 @@ class TestTimeout < Test::Unit::TestCase 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 -- cgit v1.2.3