Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions lib/timeout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -212,17 +212,17 @@ def self.synchronize(mutex, &block)
# value of 0 or +nil+ will execute the block without any timeout.
# Any negative number will raise an ArgumentError.
# +klass+:: Exception Class to raise if the block fails to terminate
# in +sec+ seconds. Omitting will use the default, Timeout::Error
# in +sec+ seconds. Omitting will use the default, Timeout::Error.
# +message+:: Error message to raise with Exception Class.
# Omitting will use the default, "execution expired"
# Omitting will use the default, <tt>"execution expired"</tt>.
#
# Returns the result of the block *if* the block completed before
# +sec+ seconds, otherwise raises an exception, based on the value of +klass+.
#
# The exception raised to terminate the given block is the given +klass+, or
# Timeout::ExitException if +klass+ is not given. The reason for that behavior
# is that Timeout::Error inherits from RuntimeError and might be caught unexpectedly by `rescue`.
# Timeout::ExitException inherits from Exception so it will only be rescued by `rescue Exception`.
# is that Timeout::Error inherits from RuntimeError and might be caught unexpectedly by +rescue+.
# Timeout::ExitException inherits from Exception so it will only be rescued by <tt>rescue Exception</tt>.
Comment on lines +224 to +225
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand those cases will be fixed by @st0012's work to better support Markdown in RDoc, is that correct?
If so I'm not so inclined to have HTML tags here, it looks pretty messy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will fix the rendering though, and the other changes look good so I guess let's go with that for now, and then maybe go back to backticks once RDoc supports them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah if we update RDoc to 7.10 then we can use backticks in rdoc markup too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know when that would happen for ruby/ruby? (since it doesn't seem to work for https://docs.ruby-lang.org/en/master/Timeout.html#method-c-timeout)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting because the rescue one line above is now correctly rendered. And this line is rendered as ‘rescue Exception`. I wonder if the beginning quote is actually not a backtick character 🤔

Copy link
Member

@tompng tompng Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In RDoc format, Only `single_word` is allowed mainly because of the restriction using regexp and gsub in parsing (I think).
Remaining backticks and quotes are converted to multibyte quotes ‘’”“

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that's unfortunate, is there already an issue about that? (I didn't find one, only related ruby/rdoc#1230)
What's the correct workaround for now for multi-word code quoting? <tt>some code</tt>?

# Note that the Timeout::ExitException is translated to a Timeout::Error once it reaches the Timeout.timeout call,
# so outside that call it will be a Timeout::Error.
#
Expand All @@ -231,21 +231,21 @@ def self.synchronize(mutex, &block)
# For those reasons, this method cannot be relied on to enforce timeouts for untrusted blocks.
#
# If a scheduler is defined, it will be used to handle the timeout by invoking
# Scheduler#timeout_after.
# Fiber::Scheduler#timeout_after.
#
# 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().
#
# ==== Ensuring the exception does not fire inside ensure blocks
#
# When using Timeout.timeout it can be desirable to ensure the timeout exception does not fire inside an +ensure+ block.
# The simplest and best way to do so it to put the Timeout.timeout call inside the body of the begin/ensure/end:
# When using Timeout.timeout, it can be desirable to ensure the timeout exception does not fire inside an +ensure+ block.
# The simplest and best way to do so is to put the Timeout.timeout call inside the body of the +begin+/+ensure+/+end+:
#
# begin
# Timeout.timeout(sec) { some_long_operation }
# ensure
# cleanup # safe, cannot be interrupt by timeout
# cleanup # safe, cannot be interrupted by timeout
# end
#
# If that is not feasible, e.g. if there are +ensure+ blocks inside +some_long_operation+,
Expand All @@ -263,17 +263,17 @@ def self.synchronize(mutex, &block)
# end
# }
#
# An important thing to note is the need to pass an exception klass to Timeout.timeout,
# otherwise it does not work. Specifically, using +Thread.handle_interrupt(Timeout::ExitException => ...)+
# An important thing to note is the need to pass an exception +klass+ to Timeout.timeout,
# otherwise it does not work. Specifically, using <tt>Thread.handle_interrupt(Timeout::ExitException => ...)</tt>
# is unsupported and causes subtle errors like raising the wrong exception outside the block, do not use that.
#
# Note that Thread.handle_interrupt is somewhat dangerous because if setup or cleanup hangs
# then the current thread will hang too and the timeout will never fire.
# Also note the block might run for longer than +sec+ seconds:
# e.g. some_long_operation executes for +sec+ seconds + whatever time cleanup takes.
# e.g. +some_long_operation+ executes for +sec+ seconds + whatever time cleanup takes.
#
# If you want the timeout to only happen on blocking operations one can use :on_blocking
# instead of :immediate. However, that means if the block uses no blocking operations after +sec+ seconds,
# If you want the timeout to only happen on blocking operations, one can use +:on_blocking+
# instead of +:immediate+. However, that means if the block uses no blocking operations after +sec+ seconds,
# the block will not be interrupted.
def self.timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+
return yield(sec) if sec == nil or sec.zero?
Expand Down