From f6180389d2a205bc655606d65c458b05fb55e44d Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Fri, 11 Feb 2011 16:40:27 +0900 Subject: [PATCH] timeout fix Current timeout is racy. Now, timeout module has following code. ------------------------------------------------------------------------------- def timeout() begin x = Thread.current y = Thread.start { begin sleep sec rescue => e x.raise e else x.raise exception, "execution expired" if x.alive? end } return yield(sec) rescue exception => e raise Error, e.message, e.backtrace ensure if y and y.alive? // (1) y.kill y.join # make sure y is dead. end end end ------------------------------------------------------------------------------- Then, A following race can occur. CPU0(thread x) CPU1(thread y) remark --------------------------------------------------------------------------- begin Thread.start sleep sec evaluate [user-defined-block] y.alive? return true wakeup from sleep x.raise Now, x is running at (1). Then ExitException which y raised can't be handled above rescue block. Then eventually, ExitException leak to caller and makes application exit. This patch fixes it. --- lib/timeout.rb | 32 +++++++++++++++++--------------- 1 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index 297b769..aa0e757 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -44,17 +44,24 @@ module Timeout return yield(sec) if sec == nil or sec.zero? 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" if x.alive? + 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) + end rescue exception => e rej = /\A#{Regexp.quote(__FILE__)}:#{__LINE__-4}\z/o (bt = e.backtrace).reject! {|m| rej =~ m} @@ -66,11 +73,6 @@ module Timeout raise if klass # if exception class is specified, it # would be expected outside. raise Error, e.message, e.backtrace - ensure - if y and y.alive? - y.kill - y.join # make sure y is dead. - end end end -- 1.6.5.2