Coder Social home page Coder Social logo

timeout's Introduction

Timeout

Timeout provides a way to auto-terminate a potentially long-running operation if it hasn't finished in a fixed amount of time.

Previous versions didn't use a module for namespacing, however #timeout is provided for backwards compatibility. You should prefer Timeout.timeout instead.

Installation

Add this line to your application's Gemfile:

gem 'timeout'

And then execute:

$ bundle

Or install it yourself as:

$ gem install timeout

Usage

require 'timeout'
status = Timeout::timeout(5) {
  # Something that should be interrupted if it takes more than 5 seconds...
}

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/timeout.

timeout's People

Contributors

hsbt avatar nobu avatar jjb avatar eregon avatar znz avatar jeremyevans avatar ioquatix avatar akr avatar drbrain avatar dependabot[bot] avatar k0kubun avatar olleolleolle avatar byroot avatar amatsuda avatar larskanis avatar mark-young-atg avatar gamecreature avatar tricknotes avatar mame avatar ko1 avatar nurse avatar rm155 avatar shyouhei avatar tarui avatar

Stargazers

Duncan Gough avatar Mohnish Thallavajhula avatar Sebastián Orellana avatar Magnus Jurdal avatar Lucas Paiva avatar Peter Boling avatar Ricardo Mejía O. avatar lidashuang avatar Robert Pannick avatar Tarun Chaudhry avatar Ivan Goshevski avatar Andrey Marchenko avatar Marko Kajzer avatar Joel Ibaceta avatar Elisson Guímel avatar  avatar バカです avatar Vladimir Kochnev avatar Chongchen Chen avatar Diego Linhares avatar saki avatar Paulo Phagula avatar Sim Teck Lim avatar Manabu Niseki avatar Alef ojeda de Oliveira avatar Clément Morisset avatar Kassam Housseny avatar Risal Hidayat avatar MingyuanQin avatar Azeem Sajid avatar Juri Hahn avatar Anthony avatar Matheus Lopes avatar 0xRoy avatar website avatar Pedro Perafán avatar Umit Kitapcigil avatar Arturs Krapans avatar Wenderson Fernandes avatar Yasar Celep avatar Pastorinni Ochoa avatar Nicholas Krupenin avatar Tejas avatar Nick K. avatar Davide Merli avatar Yaroslav Melnychuk avatar Fred (sung) avatar Arturs Krapans avatar Ryo S. avatar Eddie Kao avatar Gabriel S. avatar  avatar Suraj Nath avatar Evan avatar Felipe Orlando avatar  avatar Erol Uysal avatar İrfan Subaş avatar Ev Dolzhenko avatar Alexander Delgado avatar Nick Spain avatar Bryden Wayne avatar  avatar Esteban avatar Zheng PiaoDan avatar Yukitaka Iha avatar Adonis avatar Gonçalo Amaral avatar Federico Moyá  avatar Leonardo Barroeta avatar Kaio Silveira avatar Ian Lynx avatar George Asfour avatar KAK avatar Diego Carrion avatar Gary Tou avatar Eric Schultz avatar Max Chernyak avatar Vladislav Andreev avatar Ezequiel Maraschio avatar Thanos Bellos avatar George Bellos avatar Ahmed Bouhuolia avatar Quentin Rousseau avatar Nam Ho avatar Marian Kostyk avatar Luiz Eduardo Kowalski avatar Rajesh Rajappan avatar Şafak Ferhat Kaya avatar Jacky Alcine avatar Dmitry Polushkin avatar Robin avatar kugle064 avatar André Bianchi avatar stoneware.dev avatar Ram avatar Jits avatar Brandon Hicks avatar Michael Basmanov avatar Alex Kholodniak avatar

Watchers

 avatar Kenta Murata avatar Yuki Yugui Sonoda avatar  avatar Charles Oliver Nutter avatar Akinori Musha avatar  avatar  avatar  avatar  avatar  avatar usa avatar  avatar Takeyuki FUJIOKA avatar Masatoshi SEKI avatar Kentaro Goto avatar Hiroshi Nakamura avatar Jun Aruga avatar  avatar Martin Bosslet avatar Martin Dürst avatar James Cloos avatar Kazuki Yamaguchi avatar Marcus Stollsteimer avatar aycabta avatar Masataka Pocke Kuwabara avatar Kazuki Tanaka avatar Yuichiro Kaneko avatar Hiroya Fujinami avatar  avatar Rustam Ibragimov avatar Magnus Jurdal avatar

timeout's Issues

Timeout fails with No live threads left. Deadlock? fatal error after making any API call with Faraday

This only occurs on Windows machines, to my knowledge and only in version 0.3.x

Script that reproduces this
timeout_error.rb

require 'faraday'
require 'byebug'
temp = Faraday.get("https://google.com") # any api call, whether successful or not
byebug # *** while in byebug, tap up arrow several times to access Readline::HISTORY through byebug/interface
exit 0

On a windows machine, run the script
./timeout_error.rb

(byebug) *** CLICK UP ARROW A FEW TIMES ***
Traceback (most recent call last):
        26: from C:/sample_script/timeout_error.rb:6:in `<main>'
        25: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/context.rb:98:in `at_line'
        24: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/command_processor.rb:55:in `at_line'
        23: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/command_processor.rb:97:in `process_commands'
        22: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/command_processor.rb:128:in `repl'
        21: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interface.rb:38:in `read_command'
        20: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interface.rb:55:in `read_input'
        19: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interface.rb:71:in `prepare_input'
        18: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb:24:in `readline'
        17: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb:36:in `with_repl_like_sigint'
        16: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb:24:in `block in readline'
        15: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb:53:in `without_readline_completion'
        14: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb:24:in `block (2 levels) in readline'
        13: from C:/Ruby27-x64/lib/ruby/2.7.0/forwardable.rb:235:in `readline'
        12: from C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:188:in `readline'
        11: from C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:238:in `inner_readline'
        10: from C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:238:in `loop'
         9: from C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:239:in `block in inner_readline'
         8: from C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:270:in `read_io'
         7: from C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:270:in `loop'
         6: from C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:285:in `block in read_io'
         5: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:198:in `timeout'
         4: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `catch'
         3: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `catch'
         2: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `block in catch'
         1: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:184:in `block in timeout'
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:184:in `synchronize': No live threads left. Deadlock? (fatal)
2 threads, 2 sleeps current:0x000001fdbd516810 main thread:0x000001fdbd516810
* #<Thread:0x000001fdbf05c3f0 sleep_forever>
   rb_thread_t:0x000001fdbd516810 native:0x000000000000026c int:1
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:184:in `synchronize'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:184:in `block in timeout'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `block in catch'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `catch'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `catch'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:198:in `timeout'
   C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:285:in `block in read_io'
   C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:270:in `loop'
   C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:270:in `read_io'
   C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:239:in `block in inner_readline'
   C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:238:in `loop'
   C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:238:in `inner_readline'
   C:/Ruby27-x64/lib/ruby/2.7.0/reline.rb:188:in `readline'
   C:/Ruby27-x64/lib/ruby/2.7.0/forwardable.rb:235:in `readline'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb:24:in `block (2 levels) in readline'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb:53:in `without_readline_completion'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb:24:in `block in readline'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb:36:in `with_repl_like_sigint'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interfaces/local_interface.rb:24:in `readline'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interface.rb:71:in `prepare_input'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interface.rb:55:in `read_input'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/interface.rb:38:in `read_command'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/command_processor.rb:128:in `repl'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/command_processor.rb:97:in `process_commands'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/processors/command_processor.rb:55:in `at_line'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/byebug-11.1.3/lib/byebug/context.rb:98:in `at_line'
   C:/sample_script/timeout_error.rb:6:in `<main>'
* #<Thread:0x000001fdc2d1bca0@Timeout stdlib thread C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:101 sleep_forever>
   rb_thread_t:0x000001fdc3040810 native:0x00000000000000a4 int:0
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:113:in `wait'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:113:in `block (2 levels) in create_timeout_thread'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:111:in `synchronize'
   C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/timeout-0.3.2/lib/timeout.rb:111:in `block in create_timeout_thread'

Idea: a standard "shutdown signal"

To time out gracefully, the inner code needs to be told stop doing work. graphql-ruby has such a timeout

This can already be done with Timeout using 2 blocks, with the inner one sending a special exception, as shown below. This doesn't solve things like "what if the timeout exception is raised inside an else block", but I think the venn diagram is such that if ShutdownRequest is caught and the code comes back, it's safe.

require 'timeout'

class ShutdownRequest < StandardError; end
class ShutdownHandledGracefully < StandardError; end

def do_work
  1000.times do |n|
    puts n
    sleep 1
  rescue ShutdownRequest
    puts "shuting down gracefully..."
    raise ShutdownHandledGracefully
  end
ensure
  puts "cleaning up resources..."
  sleep 2
  puts "...done"
end

Timeout.timeout(3, ShutdownRequest) do
  do_work
rescue ShutdownHandledGracefully
  puts "nice."
end

puts
puts

# give it 1 second to try to shutdown gracefully, then force it
begin
  Timeout.timeout(4) do
    Timeout.timeout(3, ShutdownRequest) do
      do_work
    end
  end
rescue ShutdownHandledGracefully
  puts "nice."
rescue Timeout::Error
  puts "shutdown was not graceful, need to kill the process so there aren't broken resources..."
end
➔ ruby shutdown.rb
0
1
2
shuting down gracefully...
cleaning up resources...
...done
nice.


0
1
2
shuting down gracefully...
cleaning up resources...
shutdown was not graceful, need to kill the process so there aren't broken resources...

This is often not practical because the code we want to time out is many layers of libraries we don't control, and can't accommodate our special shutdown method.

If this pattern is indeed safe, maybe the timeout gem could formalize it, by:

a. defining its own Shutdown class
b. allowing specification of both "shutdown signal time" and "strict time", which would implement the double layer pattern shown above

Timeout.timeout_with_grace_period(soft: 3, hard: 5){ }

Then, libraries and application code could support this concept. For example, rails ActiveRecord::Relation#find_each could rescue ShutdownRequest, then raise ShutdownHandledGracefully. if the outer code doesn't catch that, that's good - it will roll back a transaction and work through any chain of ensures so things are cleaned up. if it all happens within the soft time, then the state is clean. If not, then the process should be killed. (as with pitchfork or rack-timeout term_on_timeout.

If there is some reluctance to code the hard/soft functionality, but the pattern is sound, maybe as a first step, the class could simply be defined. Timeout::Shutdown - this gives implementers at all levels the opportunity to start supporting the pattern, referencing the same class.

Changelog?

Hi, dependabot notify me that v0.3.0 is released

Any plans that there will human-readble changelog, not only git commit changes?

Thread.handle_interrupt results in an unpredictable behavior since #15

tl; dr

Thread.handle_interrupt won't work well when the timeout thread is reused. It is because blocked interrupts are inherited when the thread is created. In my opinion such an inheritance should not happen, but removing it will be a breaking change and requires core Ruby change.

Long story

#15 changed Timeout to reuse a thread. On the other hand, a thread inherits the blocked interrupts of the creating thread. This means the list of blocked interrupts will persist once after a thread is created first time.

This breaks the code something like follows:

# Part X
timeout(10) {}

# Part Y
Thread.handle_interrupt(Timeout::Error => :never) do
  timeout(10) do
    Thread.handle_interrupt(Timeout::Error => :on_blocking) do
      do_something
    end
  ensure
    clean # Timeout::Error shouldn't occur here (but it can since #15)
  end
end

Code Y follows the example shown at: https://docs.ruby-lang.org/en/3.2/Thread.html#method-c-handle_interrupt-label-Guarding+from+Timeout-3A-3AError
In this case, the later Thread.handle_interrupt(Timeout::Error => :never) will have no effect.

The opposite scenario can also happen. Consider the following code:

# Part Y
Thread.handle_interrupt(Timeout::Error => :never) do
  timeout(10) do
    Thread.handle_interrupt(Timeout::Error => :on_blocking) do
      do_something
    end
  ensure
    clean
  end
end

# Part X
timeout(10) do
  # Timeout::Error should happen here but it won't in reality.
end

In my opinion, this kind of problem occurs due to a bad design of Ruby threads; a new thread should not inherit the blocked interrupts. The inheritance of blocked interrupts can break multithreading code so easily. Thread reuse as seen in Timeout is one of patterns affected by this design. Any kind of thread pools will also be affected.

The inheritance is also problematic when a method called from Thread.handle_interrupt block uses Thread#raise. For example, think of the following code:

module SomeExternalLibrary
  def gracefully_close_connection
    begin
      timeout(10) { tell_the_remote_machine_that_i_am_leaving }
    rescue Timeout::Error
      # I guess the remote machine is dead.
    end
    @@socket.close
  end
end

# I want to make sure graceful shutdown happens even when SIGINT happens.
Thread.handle_interrupt(Object => :never) do
  SomeExternalLibrary.gracefully_close_connection
end

SomeExternalLibrary.gracefully_close_connection may not work because timeout is blocked with Thread.handle_interrupt(Object => :never).

This kind of problems will not happen if the inheritance of blocked interrupts does not happen. Removing the inheritance of blocked interrupts would break code part Y, but I don't think that matters much. In fact, if you should be able to rewrite the code as follows:

begin
  timeout(10) do
    do_something
  end
ensure
  clean # Here Timeout::Error won't happen because it's out of the timeout block.
end

If you still need to block Timeout::Error at the beginning of the timeout block, timeout method can have an extra parameter to block interrupts before the timer gets armed though I don't see a use case for that.

That said, removing the inheritance of blocked interrupts will be a breaking change so it's not really practical. Also it will be a core Ruby change so timeout gem may need its own solution, perhaps just reverting #15, adding an option, or just edit the documentation to say "do not use timeout with Thread.handle_interrupt ever".

Timeout cannot be called in a trap handler as of 0.3.0

As of Timeout 0.3.0 you can no longer use Timeout in a signal trap handler due to Mutex#synchronize not being callable inside a trap context. I haven't done a git bisect but I believe this was broken in 5e0d8e1 due to the addition of a mutex on @done.

This works fine with 0.2.0.

require "timeout"

rd, wr = IO.pipe

trap("SIGUSR1") do
  Timeout.timeout(1) do
  end

  # Close the pipe writer to unblock the main thread
  wr.close
end

# Send USR1 to the current process
Process.kill("USR1", Process.pid)

# Wait for the timeout in the signal handler
rd.read
rd.close

Exception

./ruby-3.1.2/gems/timeout-0.3.0/lib/timeout.rb:128:in `synchronize': can't be called from trap context (ThreadError)
	from ./ruby-3.1.2/gems/timeout-0.3.0/lib/timeout.rb:128:in `ensure_timeout_thread_created'
	from ./ruby-3.1.2/gems/timeout-0.3.0/lib/timeout.rb:171:in `timeout'
	from test.rb:6:in `block in <main>'
	from test.rb:12:in `kill'
	from test.rb:12:in `<main>'

Discussion about behavior

I have this experimental improvement to timeout: https://github.com/jjb/better_timeout/. It's a refresh of an old project and I'm still exploring its goals. For the purpose of this discussion we can ignore the gem itself.

While making the gem I made some expanded tests for stdlib timeout.

Here's the code that runs for the expanded tests: https://github.com/jjb/better_timeout/blob/master/test/error_lifecycle.rb

This file (from the linked line down) shows behavior for ruby 2.4+, including current HEAD of ruby/timeout: https://github.com/jjb/better_timeout/blob/master/test/test_timeout-2.4-3.0.rb#L67

I have the scenarios described with a comment above each test_N. I have the behavior annotated with "expected", "weird", and "bad" (my opinion, and the topic of this discussion).

There are three things I'd like to discuss.

1. should an exception always be raised in outer?

In my opinion, Timeout.timeout should always raise an exception when it times out.

If we think it shouldn't in some scenarios, then I think that behavior should be controlled by the invocation of Timeout.timeout (in a new option), not by whether or not the inner code happens to rescue Exception

These tests seem to explicitly state that internal code should be allowed to swallow all exceptions, so I'm guessing I won't have any luck trying to convince this project to change 😅 https://github.com/ruby/timeout/blob/master/test/test_timeout.rb#L34-L60

I tried translating https://bugs.ruby-lang.org/issues/8730 but didn't understand the rationale behind enforcing the not-raise behavior.

2. weird behavior in test_2

In test_2, exception to raise is not specified, but inner code does rescue Exception. Regardless of this, the inner code is not able to rescue the error. I haven't yet looked into why this is.

3. are you interested in putting my tests into the test suite?

Are you interested in a PR which puts these tests into the ruby/timeout test suite? I'll redo them so that they are more in the style of the current tests, and try to rework them so that they don't use global variables, or at least use much safer namespacing if that's not possible.

If you got this far, thanks for reading! Let me know what you think!

Moving Threads to Threadgroup::Default should not happen for enclosed ThreadGroups

Moving the Thread to ThreadGroup::Default causes issues with enclosed ThreadGroups. (this change happend in commit c4f1385)

It goes wrong in for example the airbrake-ruby gem. (airbrake/airbrake-ruby#713 (comment))

  • The airbrake-ruby notifier creates a ThreadGroup with workers.
  • It encloses this group.
  • A worker starts a Net::Http request. This request creates the timeout.
  • The timeout thread is created in the enclosed worker ThreadGroup
  • An attempt is made to move the Thread to ThreadGroup::Default
  • Which results in the following exception:
#<Thread:0x0000000107da1e40 /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/airbrake-ruby-6.2.0/lib/airbrake-ruby/thread_pool.rb:131 run> terminated with exception (report_on_exception is true):
/Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/3.2.0/timeout.rb:123:in `add': can't move from the enclosed thread group (ThreadError)
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/3.2.0/timeout.rb:123:in `create_timeout_thread'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/3.2.0/timeout.rb:134:in `block in ensure_timeout_thread_created'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/3.2.0/timeout.rb:132:in `synchronize'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/3.2.0/timeout.rb:132:in `ensure_timeout_thread_created'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/3.2.0/timeout.rb:181:in `timeout'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/3.2.0/net/http.rb:1269:in `connect'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/3.2.0/net/http.rb:1248:in `do_start'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/3.2.0/net/http.rb:1237:in `start'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/3.2.0/net/http.rb:1817:in `request'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/airbrake-13.0.3/lib/airbrake/rails/net_http.rb:11:in `block in request'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/airbrake-13.0.3/lib/airbrake/rack.rb:21:in `capture_timing'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/airbrake-13.0.3/lib/airbrake/rails/net_http.rb:10:in `request'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/airbrake-ruby-6.2.0/lib/airbrake-ruby/sync_sender.rb:49:in `send'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/airbrake-ruby-6.2.0/lib/airbrake-ruby/async_sender.rb:51:in `block in thread_pool'
	from /Users/rick/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/airbrake-ruby-6.2.0/lib/airbrake-ruby/thread_pool.rb:135:in `block in spawn_worker'	

This problem could be solved by making lib/timeout.rb:123 conditional

ThreadGroup::Default.add(watcher) unless watcher.group.enclosed?

Example to illustrate this behavior:

# This sample fails!
t1 = Thread.new {
  sleep(2)
  t2 = Thread.new { }
  puts "t2.group: #{t2.group}"
  ThreadGroup::Default.add(t2) 
  puts "Success!"
}
group = ThreadGroup.new
group.add(t1)
group.enclose
puts "t1.group: #{t1.group}"
t1.join
# This sample succeeds by adding the conditional
t1 = Thread.new {
  sleep(2)
  t2 = Thread.new { }
  puts "t2.group: #{t2.group}"
  ThreadGroup::Default.add(t2) unless t2.group.enclosed?
  puts "Success!"
}
group = ThreadGroup.new
group.add(t1)
group.enclose
puts "t1.group: #{t1.group}"
t1.join

JRuby support

JRuby's timeout is based on JVM ScheduledExecutor logic that efficiently pools threads. We would like to share the gem so we will need to merge our implementation. We will handle the work necessary.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.