Project

General

Profile

« Previous | Next » 

Revision df6b72b8

Added by Stan Lo over 2 years ago

Avoid checking interrupt when loading iseq

The interrupt check will unintentionally release the VM lock when loading an iseq.
And this will cause issues with the debug gem's
ObjectSpace.each_iseq method,
which wraps iseqs with a wrapper and exposes their internal states when they're actually not ready to be used.

And when that happens, errors like this would occur and kill the debug gem's thread:

 DEBUGGER: ReaderThreadError: uninitialized InstructionSequence
┃ DEBUGGER: Disconnected.
┃ ["/opt/rubies/ruby-3.2.0/lib/ruby/gems/3.2.0/gems/debug-1.7.1/lib/debug/breakpoint.rb:247:in `absolute_path'",
┃  "/opt/rubies/ruby-3.2.0/lib/ruby/gems/3.2.0/gems/debug-1.7.1/lib/debug/breakpoint.rb:247:in `block in iterate_iseq'",
┃  "/opt/rubies/ruby-3.2.0/lib/ruby/gems/3.2.0/gems/debug-1.7.1/lib/debug/breakpoint.rb:246:in `each_iseq'",
...

A way to reproduce the issue is to satisfy these conditions at the same time:

  1. debug gem calling ObjectSpace.each_iseq (e.g. activating a LineBreakpoint).
  2. A large amount of iseq being loaded from another thread (possibly through the bootsnap gem).
  3. 1 and 2 iterating through the same iseq(s) at the same time.

Because this issue requires external dependencies and a rather complicated timing setup to reproduce, I wasn't able to write a test case for it.
But here's some pseudo code to help reproduce it:

require "debug/session"

Thread.new do
  100.times do
    ObjectSpace.each_iseq do |iseq|
      iseq.absolute_path
    end
  end
end

sleep 0.1

load_a_bunch_of_iseq
possibly_through_bootsnap

[Bug #19348]

Co-authored-by: Peter Zhu