Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ask ruby pry to stop all other threads

I'm trying to debug a multithreaded ruby script, the problem is when I do

binding.pry

The other threads continue sending output to the console. How do I make them stop at binding.pry and then start up again when I exit? I'm thinking there's a way to do this in .pryrc

like image 796
pguardiario Avatar asked May 28 '17 01:05

pguardiario


People also ask

How do I stop binding on pry?

To exit everything, use: exit! This should ignore all proceeding bindings. This also kills the server at the same time.

How do I use binding pry in Ruby?

The Ruby programmer can invoke the pry console during runtime by inserting the line 'binding. pry' wherever they would like to stop the program. When the interpreter hits the binding. pry, Pry will open a REPL session in the console, allowing you to test variables, return values, iterations, and more.

Why is binding pry not working?

To fix this just move the binding. pry command one line to the top and try to run your file again. If it still doesn't catch it, move the binding. pry command one more line to the top and keep doing this until your file catches the binding.


2 Answers

It sounds like you are proposing using the invocation of binding.pry to interrogate all child threads and suspend them until you end your pry session. That is not possible for technical and practical reasons. The Binding and Thread classes don't work that way, and multithreading in Ruby doesn't work that way.

Threads in Ruby can only be suspended by calling Kernel#sleep or Thread.stop. (and those are functionally equivalent) Crucially, these methods can only be invoked on the current thread. One thread cannot suspend another thread. (Thread.stop is a class method, not an instance method)

Let's look at what binding.pry actually does: objects of class Binding encapsulate the execution context at some particular place in the code and retain this context for future use. So when you put binding.pry into your code, you're telling Ruby to encapsulate the execution context for the current thread.

What that means is when you call binding.pry in the main thread the Binding object has context for the current thread and can tell itself to sleep, but the core Ruby Thread class does not allow it to tell any other threads to sleep.

Even if it did support it, it would be weird and error-prone and the cause of a lot of head-scratching. Imagine that you have code like this:

# we are in the main thread
Thread.new do
  # we are in the child thread
  foo = Foo.new(bar.fetch(:baz, {}))
  foo.save
end

# we are in the main thread
binding.pry

Because of the way Ruby handles context-switching, if binding.pry told all child threads to stop then the child thread might stop ANYWHERE in the call stack, including anywhere in the code for Foo.new or .save. Having those threads pause and resume in the middle of executing code that you did not write will cause you trouble. For example, what happens if an ActiveRecord connection from the pool is checked out and used for a SELECT query but the thread got put to sleep before it returned the connection to the pool and before it got a response? Bad stuff. Lots of bad stuff.

It sounds like the real solution for you is to change the verbosity of the child threads. If you are troubleshooting chatty code and your other threads are being noisy while you're trying to work in a single thread, then set the other threads to use, for example, a lower logging level temporarily.

like image 117
anothermh Avatar answered Oct 06 '22 14:10

anothermh


If binding.pry is yielding inconsistent results, try the following:

  1. If it exists, remove pry-stack_explorer from your gemfile and then repackage your application. That gem appears to have issues with pry-byebug.

  2. Additionally, not sure if you tried this already but a few server restarts couldn't hurt; this has resolved weird issues like this for me more often than I'd like to admit.

However, you may be trying to do something that simply cannot be done with binding.pry:

One fundamental about binding.pry that must be understood is that its scope would include only the current thread, not every thread in a multi-threaded application as you have described.

Hypothetically, if binding.pry did affect every thread (again, it does not), this would result in unpredictable behavior especially if some threads are performing data access/retrieval/update operations.

To accomplish what you want, you may need to take a different approach:

Although it may be tedious depending on how many threads are in your application, you may need to control/stop each thread individually. Obviously, this can be done with Thread.stop.

like image 37
lax1089 Avatar answered Oct 06 '22 15:10

lax1089