Open zach opened 10 years ago
+1 I get exactly the same errors when trying to rescue a hanging rspec test using rescue rspec and Ctrl-\ in ruby 2.1, pry-rescue 1.2.0.
I just did bundle update to pry-rescue 1.3.1 and interception 0.4. It's still failing, but the output changed a bit:
^\Preparing to peek via pry!
Error loading ./.pryrc: can't be called from trap context
/home/jakub/.rvm/gems/ruby-2.1.0@objednavky/gems/activesupport-4.0.1/lib/active_support/dependencies.rb:229:in `require'
require 'pry-doc' # Failed, saying: can't be called from trap context
require 'pry-docmore' # Failed, saying: can't be called from trap context
require 'pry-rails' # Failed, saying: can't be called from trap context
Frame number: 0/54
Frame type: method
before_session hook failed: RegexpError: empty char-class: /^define_singleton_method\(?\s*[:\"\'][]|^def\s*self\.[]/
/home/jakub/.rvm/gems/ruby-2.1.0@global/gems/pry-0.9.12.4/lib/pry/method.rb:182:in `singleton_method_definition?'
(see _pry_.hooks.errors to debug)
before_session hook failed: ThreadError: can't be called from trap context
/home/jakub/.rvm/gems/ruby-2.1.0@objednavky/gems/interception-0.4/lib/interception.rb:64:in `synchronize'
(see _pry_.hooks.errors to debug)
Not really sure what the last line means, but any command after this fails. Can provide more info if you tell me where to look.
I found what seems to be the root cause, which is that Ruby has a very limited ability to perform I/O (in this case, apparently including reading files via require
) inside signal handlers in Ruby 2.0. In 1.9.x performing such actions could cause deadlock, so in 2.0 they are simply not allowed. Thanks to @mperham I was able to pick up on this quickly, since he wrote up his experience encountering this in Sidekiq:
http://www.mikeperham.com/2013/02/23/signal-handling-with-ruby/
There are some workarounds for libraries like Foreman (see https://github.com/ddollar/foreman/issues/332 for their pipe-based solution), but they don't have to interrupt arbitrary code in its current execution state. I don't know enough about the details of pry and ruby execution state to say whether there might be a workaround for pry-rescue.
This looks like a tough one. Any ideas?
Is there anything we can do in a signal handler to escape? e.g. Signal.trap{ <capture bindings>; Thread.new{ <go wild> }.join }
Update: Guard has tackled this issue as well using a separate thread. I don't think it has access to the current binding, but that's a lot to ask. https://github.com/guard/guard/issues/571
As done by @mperham the trap should just add signals to a queue (an array or, better, a queue*) and then you can keep a loop inside a thread that will pop from the event queue.
* the queue is better because avoids using sleep by using the blocking #pop
. Using #sleep
of course is fine in sidekiq but not in a user facing app
@mperham ouch! https://twitter.com/elia/status/477581841361534976 :frog:
Similar backtrace:
[5] pry(Timeout)> yield(0)
^\Preparing to peek via pry!
ThreadError: can't be called from trap context
from /data/ruby/lib/ruby/2.4.0/monitor.rb:187:in `lock'
[6] pry(Timeout)> bt
=> nil
[7] pry(Timeout)> backtrace
--> #0 #<Class:Timeout>.timeout(sec#Float, klass#NilClass, message#NilClass) at /spool/ruby/lib/ruby/2.4.0/timeout.rb:77
And the full trace:
#<ThreadError: can't be called from trap context>
/data/ruby/lib/ruby/2.4.0/monitor.rb:187:in `lock'
/data/ruby/lib/ruby/2.4.0/monitor.rb:187:in `mon_enter'
/data/ruby/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:40:in `require'
/data/ruby/lib/ruby/gems/2.4.0/gems/pry-rescue-1.4.5/lib/pry-rescue/peek.rb:10:in `peek!'
(and then mixed code with gems: event-machine, aspector, timeout)
I have had poor luck with getting peek to work via Ctrl-\ since every
require
statement seems to cause an error, worst of all being those in Pry:before_session
hooks and those from CodeRay autoloads. As a result, I never get to see the code I'm trying to peek at, nor can I explore the stack or the other stuff. I really like this functionality and would love to make this work, but ifrequire
doesn't work I don't understand how it ever did.Here are some example sessions:
(hangs)
This is in Ruby 2.0 on OS X 10.9. Maybe there's something I don't have configured properly, but even a simple script that I break into with Ctrl-\ has this problem. I made a repo with this example (run main.rb and use Ctrl-\ or just run the test script and peek after it prints the first line of numbers):
https://github.com/zach/pry-rescue-peek-test
Why does this seem to work in other circumstances and yet I can't seem to make this work? Is this a Bundler-related issue?