Skip to content

Commit 23958ba

Browse files
committed
fix: deadlock using IRB integration on Rails applications
When using `debugger` in a rails console using IRB integration, the second `continue` causes a fatal deadlock: "No live threads left. Deadlock?" The root cause is that `leave_subsession` pops `@subsession_stack` and sets `@tc = nil` before `thread_stopper.disable` takes full effect. In that window, the thread_stopper TracePoint fires on the main thread (back in IRB's eval loop), and since `@tc` is nil, the guard `tc == @tc` no longer protects it. The main thread is paused via `on_pause`, but the session server is already blocked on `@q_evt.pop` -- neither can wake the other. The fix for this is addiing `next unless in_subsession?` as the first guard in thread_stopper. Once `leave_subsession` has popped the stack, in_subsession? returns false and the TracePoint becomes a no-op even if it hasn't been disabled yet.
1 parent 2897eda commit 23958ba

File tree

1 file changed

+9
-0
lines changed

1 file changed

+9
-0
lines changed

lib/debug/session.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,6 +1678,15 @@ def get_thread_client th = Thread.current
16781678

16791679
private def thread_stopper
16801680
TracePoint.new(:line) do
1681+
# When leave_subsession pops the subsession stack and sets @tc = nil,
1682+
# there is a window before the TracePoint is disabled where this block
1683+
# can still fire on other threads. Without this guard, the main thread
1684+
# (back in IRB's eval loop) would be paused by on_pause, but the
1685+
# session server is already waiting on @q_evt.pop -- causing a mutual
1686+
# deadlock ("No live threads left. Deadlock?").
1687+
# See: https://github.com/ruby/debug/issues/1158
1688+
next unless in_subsession?
1689+
16811690
# run on each thread
16821691
tc = ThreadClient.current
16831692
next if tc.management?

0 commit comments

Comments
 (0)