* lib/webrick/utils.rb (WEBrick::Utils::TimeoutHandler): Acquire

TimeoutMutex only when accessing @timeout_info for avoiding
  potential deadlock. [Bug #11742] [ruby-dev:49387]


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@53134 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ngoto 2015-12-15 16:03:00 +00:00
parent 00f9a74bca
commit e337dc6517
2 changed files with 28 additions and 21 deletions

View File

@ -1,3 +1,9 @@
Wed Dec 16 00:53:45 2015 Naohisa Goto <ngotogenome@gmail.com>
* lib/webrick/utils.rb (WEBrick::Utils::TimeoutHandler): Acquire
TimeoutMutex only when accessing @timeout_info for avoiding
potential deadlock. [Bug #11742] [ruby-dev:49387]
Wed Dec 16 00:39:27 2015 Jake Worth <jakeworth82@gmail.com> Wed Dec 16 00:39:27 2015 Jake Worth <jakeworth82@gmail.com>
* doc/extension.rdoc: [DOC] fix double-word typo. [Fix GH-1153] * doc/extension.rdoc: [DOC] fix double-word typo. [Fix GH-1153]

View File

@ -135,24 +135,22 @@ module WEBrick
# +time+:: Timeout in seconds # +time+:: Timeout in seconds
# +exception+:: Exception to raise when timeout elapsed # +exception+:: Exception to raise when timeout elapsed
def TimeoutHandler.register(seconds, exception) def TimeoutHandler.register(seconds, exception)
TimeoutMutex.synchronize{ instance.register(Thread.current, Time.now + seconds, exception)
instance.register(Thread.current, Time.now + seconds, exception)
}
end end
## ##
# Cancels the timeout handler +id+ # Cancels the timeout handler +id+
def TimeoutHandler.cancel(id) def TimeoutHandler.cancel(id)
TimeoutMutex.synchronize{ instance.cancel(Thread.current, id)
instance.cancel(Thread.current, id)
}
end end
## ##
# Creates a new TimeoutHandler. You should use ::register and ::cancel # Creates a new TimeoutHandler. You should use ::register and ::cancel
# instead of creating the timeout handler directly. # instead of creating the timeout handler directly.
def initialize def initialize
@timeout_info = Hash.new TimeoutMutex.synchronize{
@timeout_info = Hash.new
}
@watcher = Thread.start{ @watcher = Thread.start{
to_interrupt = [] to_interrupt = []
while true while true
@ -185,11 +183,9 @@ module WEBrick
## ##
# Interrupts the timeout handler +id+ and raises +exception+ # Interrupts the timeout handler +id+ and raises +exception+
def interrupt(thread, id, exception) def interrupt(thread, id, exception)
TimeoutMutex.synchronize{ if cancel(thread, id) && thread.alive?
if cancel(thread, id) && thread.alive? thread.raise(exception, "execution timeout")
thread.raise(exception, "execution timeout") end
end
}
end end
## ##
@ -198,8 +194,11 @@ module WEBrick
# +time+:: Timeout in seconds # +time+:: Timeout in seconds
# +exception+:: Exception to raise when timeout elapsed # +exception+:: Exception to raise when timeout elapsed
def register(thread, time, exception) def register(thread, time, exception)
@timeout_info[thread] ||= Array.new info = nil
@timeout_info[thread] << (info = [time, exception]) TimeoutMutex.synchronize{
@timeout_info[thread] ||= Array.new
@timeout_info[thread] << (info = [time, exception])
}
begin begin
@watcher.wakeup @watcher.wakeup
rescue ThreadError rescue ThreadError
@ -210,14 +209,16 @@ module WEBrick
## ##
# Cancels the timeout handler +id+ # Cancels the timeout handler +id+
def cancel(thread, id) def cancel(thread, id)
if ary = @timeout_info[thread] TimeoutMutex.synchronize{
ary.delete_if{|info| info.object_id == id } if ary = @timeout_info[thread]
if ary.empty? ary.delete_if{|info| info.object_id == id }
@timeout_info.delete(thread) if ary.empty?
@timeout_info.delete(thread)
end
return true
end end
return true return false
end }
return false
end end
end end