Issue #19348 has been updated by naruse (Yui NARUSE).
Backport changed from 2.7: DONTNEED, 3.0: DONTNEED, 3.1: DONTNEED, 3.2: REQUIRED to 2.7:
DONTNEED, 3.0: DONTNEED, 3.1: DONTNEED, 3.2: DONE
ruby_3_2 0090cb82b0bf477c29a659e34cf4427a3b1ceb27 merged revision(s)
df6b72b8ff7af16a56fa48f3b4abb1d8850f4d1c.
----------------------------------------
Bug #19348: GVL being released earlier than expected when loading iseqs
https://bugs.ruby-lang.org/issues/19348#change-101451
* Author: st0012 (Stan Lo)
* Status: Closed
* Priority: Normal
* ruby -v: 3.2.0
* Backport: 2.7: DONTNEED, 3.0: DONTNEED, 3.1: DONTNEED, 3.2: DONE
----------------------------------------
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, 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
`](https://github.com/ruby/ruby/blob/9399352a43253e2905d76d21774fb0301069197b/compile.c#L12125)
function underneath.
2. After commit
[
e35c528d721d209ed8531b10b46c2ac725ea7bf5](https://github.com/ruby/ruby/comm…
(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`](https://github.com/ruby/debug/blob/0fcfc28acae33ec1c08068fb7c33703cfa681fa7/ext/debug/iseq_collector.c#L61-L67)
to [activate a
`LineBreakpoint`](https://github.com/ruby/debug/blob/0fcfc28acae33ec1c08068fb7c33703cfa681fa7/lib/debug/breakpoint.rb#L246)
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](https://github.com/ruby/ruby/blob/40a9964b893fee5680b455d0e905155be3….
--
https://bugs.ruby-lang.org/