The following example is given in the book Programming Ruby 1.9 & 2.0:
require 'irb'
trap "INT" do
IRB.start
end
count = 0
loop do
count += 1
puts count
puts "Value = #{@value}" if defined? @value
sleep 1
end
Unfortunately, if I run this code with my Ruby 2.5.1 setup I get the following ThreadError (run_irb.rb is the name of the file with the code above):
24: from run_irb.rb:8:in `<main>'
23: from run_irb.rb:8:in `loop'
22: from run_irb.rb:12:in `block in <main>'
21: from run_irb.rb:12:in `sleep'
20: from run_irb.rb:4:in `block in <main>'
19: from /usr/lib/ruby/2.5.0/irb.rb:376:in `start'
18: from /usr/lib/ruby/2.5.0/irb/init.rb:17:in `setup'
17: from /usr/lib/ruby/2.5.0/irb/init.rb:112:in `init_config'
16: from /usr/lib/ruby/2.5.0/irb/init.rb:112:in `new'
15: from /usr/lib/ruby/2.5.0/irb/locale.rb:32:in `initialize'
14: from /usr/lib/ruby/2.5.0/irb/locale.rb:108:in `load'
13: from /usr/lib/ruby/2.5.0/irb/locale.rb:124:in `find'
12: from /usr/lib/ruby/2.5.0/irb/locale.rb:145:in `search_file'
11: from /usr/lib/ruby/2.5.0/irb/locale.rb:157:in `each_localized_path'
10: from /usr/lib/ruby/2.5.0/irb/locale.rb:167:in `each_sublocale'
9: from /usr/lib/ruby/2.5.0/irb/locale.rb:158:in `block in each_localized_path'
8: from /usr/lib/ruby/2.5.0/irb/locale.rb:150:in `block in search_file'
7: from /usr/lib/ruby/2.5.0/rubygems.rb:213:in `try_activate'
6: from /usr/lib/ruby/2.5.0/rubygems/specification.rb:1063:in `find_by_path'
5: from /usr/lib/ruby/2.5.0/rubygems/specification.rb:1063:in `find'
4: from /usr/lib/ruby/2.5.0/rubygems/specification.rb:1063:in `each'
3: from /usr/lib/ruby/2.5.0/rubygems/specification.rb:1064:in `block in find_by_path'
2: from /usr/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:40:in `require'
1: from /usr/lib/ruby/2.5.0/monitor.rb:185:in `mon_enter'
/usr/lib/ruby/2.5.0/monitor.rb:185:in `lock': can't be called from trap context (ThreadError)
Is there a way to get the irb run from the trap context with Ruby 2.5.1?
Ruby 2.0 does not allow Mutex#lock within a signal handler
According to this bug 7917 in ruby lang this is the "expected" behaviour. So you've basically found a language bug and a book bug because the author didn't test this example in ruby 2.0 (confirmed that it works in 1.9.)
An alternative method is to use an exception/rescue and execute an IRB session from an interrupt like this:
require 'irb'
count = 0
loop do
count += 1
puts count
puts "Value = #{@value}" if defined? @value
sleep 1
rescue Interrupt => e
IRB.start
break
end
The above will correctly trap an interrupt and allow an IRB session to start. I did notice that IRB.start won't give you a local execution context, so you may want to look at binding.irb that let's you start a more useful debugging session like this:
$ ruby thread-test.rb
1
2
^C
[1] irb(main)>
From: /work/_scratch/thread-test/thread-test.rb @ line 11 :
6: puts count
7: puts "Value = #{@value}" if defined? @value
8: sleep 1
9: rescue Interrupt => e
10: binding.irb
=> 11: break
12: end
[2] irb(main)> e
=> Interrupt
[3] irb(main)> count
=> 2
I found a few blog posts on handling Signals, Traps, and Rescues and Graceful Shutdown that may help you further with your problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With