Project

General

Profile

Actions

Bug #19348

closed

GVL being released earlier than expected when loading iseqs

Added by st0012 (Stan Lo) over 1 year ago. Updated over 1 year ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:111849]

Description

When using the debug gem in a Rails app with Ruby 3.2, I noticed that if the VS Code editor connects to the debugger during the app boot, this error could occur:

 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'",
...

After investigating it with @peterzhu2118 (Peter Zhu), we found that it's because:

  1. During the Rails app's boot time, it uses bootsnap to load iseqs, which uses the ibf_load_iseq_each function underneath.
  2. After commit e35c528d721d209ed8531b10b46c2ac725ea7bf5 (added in 3.2), that function starts calling rb_vm_pop_frame at the end of execution.
  3. Because rb_vm_pop_frame triggers the release of GVL, iseqs that just being loaded now become accessible by other threads, even though they're not ready to be used.
  4. Now, if the debug gem calls ObjectSpace.each_iseq to activate a LineBreakpoint from its own thread, it'd gain access to those unready iseqs and try to read their state, which would then cause the uninitialized InstructionSequence error.
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0