Project

General

Profile

Actions

Bug #22442

closed

Lazy loading of error_highlight etc. is skipped in threads and Ractors

Bug #22442: Lazy loading of error_highlight etc. is skipped in threads and Ractors

Added by mame (Yusuke Endoh) 1 day ago. Updated about 16 hours ago.

Status:
Closed
Target version:
-
[ruby-core:127110]

Description

#21951 made error_highlight / did_you_mean / syntax_suggest lazily loaded.
When the lazy loading is triggered concurrently from threads or Ractors, the decoration may be skipped.

# repro.rb
rs = 2.times.map do
  Ractor.new do
    nil.upcase
  rescue => e
    e.detailed_message(highlight: false)
  end
end
rs.each { puts it.value, "" }

Expected:

$ ruby repro.rb
undefined method 'upcase' for nil (NoMethodError)

    nil.upcase
       ^^^^^^^

undefined method 'upcase' for nil (NoMethodError)

    nil.upcase
       ^^^^^^^

Actual:

$ ruby repro.rb
undefined method 'upcase' for nil (NoMethodError)

    nil.upcase
       ^^^^^^^

undefined method 'upcase' for nil (NoMethodError)

Cause: the first caller of detailed_message sets a flag when it starts loading. So when another thread or Ractor calls it during the load, it returns without decoration instead of waiting for the load to complete.

Fix: fire the autoload instead of calling rb_require_string, so that the autoload's synchronization prevents the skip.

https://github.com/ruby/ruby/pull/19352

Also, because rb_require_string was used, a newer version installed by gem install error_highlight was not used.
Firing the autoload respects the monkeypatch of Kernel#require, so the newer version of error_highlight will be used.

Note: this is why the snippet is not shown in #22439.


Related issues 1 (0 open — 1 closed)

Related to Ruby - Bug #22439: Segfault in `Thread::Backtrace::Location#syntax_tree` (via `Exception#detailed_message` / error_highlight) when called from multiple RactorsClosedActions

Updated by mame (Yusuke Endoh) 1 day ago Actions #1

  • Related to Bug #22439: Segfault in `Thread::Backtrace::Location#syntax_tree` (via `Exception#detailed_message` / error_highlight) when called from multiple Ractors added

Updated by mame (Yusuke Endoh) about 16 hours ago Actions #2

  • Status changed from Assigned to Closed

Applied in changeset git|d193fd3217a87b1015b73a4d31c0f6e116bc3cdd.


Load the error decoration gems by firing their autoloads

The lazy loading of error_highlight, did_you_mean, and syntax_suggest
claimed the load by an atomic exchange before the load started. So a
caller in another Ractor or thread during the load returned the
undecorated message without waiting for the load to complete.

rs = 2.times.map do
  Ractor.new do
    nil.upcase
  rescue => e
    e.detailed_message(highlight: false)
  end
end
rs.each { puts it.value, "" }
# One of them lacks the snippet by error_highlight

Load the gems with rb_autoload_load instead of rb_require_string, as a
constant reference would. The autoload's own synchronization makes
concurrent callers wait until the load is done. The flag to load the
gems only once is no longer needed: rb_autoload_load does nothing once
the gems are loaded, and whether to re-dispatch is now decided by whether
the load has just put a decorator above Exception#detailed_message.

This also makes the gems loaded through RubyGems' Kernel#require, so a
newer version of the gems installed by the user is activated, as it was
before they were lazily loaded.

[Bug #22442]

Co-Authored-By: Claude Opus 5.5

Actions

Also available in: PDF Atom