Project

General

Profile

Actions

Bug #21146

closed

VM_ASSERT(expr) gives bad bug report results when another ractor fails an assertion during printing of report

Bug #21146: VM_ASSERT(expr) gives bad bug report results when another ractor fails an assertion during printing of report

Added by luke-gru (Luke Gruber) over 1 year ago. Updated 1 day ago.

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

Description

test.rb:

rs = 100.times.map do
  Ractor.new do
    cnt = rand 3
    cnt += 1 if cnt.zero?
    sleep cnt
    100.times do |i|
      if i != 0 && i % 50 == 0
        Ractor.fail_assert
      end
    end
  end
end

ractor.rb:

def self.fail_assert
  __builtin_cexpr! %q{
    VM_ASSERT(0), Qfalse
  }
end

make run

I would like to be able to see the bug report for the first failed assertion, without any output from the other ractors.

Updated by ko1 (Koichi Sasada) over 1 year ago Actions #2 [ruby-core:121288]

Your patch uses RB_VM_LOCK_ENTER_NO_BARRIER but it should block normal use of rb_bug() (using rb_bug() is irregular case though).
So I think it should use simpler mechanism to synchronize rb_bug() calling. For example, introducing a global variable to avoid multiple rb_bug() calls.

(btw VM_ASSERT() calls rb_bug() if RUBY_DEBUG (or other macros) is defined, so rb_bug() is suitable for the example)

Updated by luke-gru (Luke Gruber) over 1 year ago ยท Edited Actions #3 [ruby-core:121300]

Thanks for your comment. I can make it simpler, but I am a bit confused as to what I should do instead. If the first thread gets to the global variable first and enters rb_vm_bugreport, my thinking was that other threads that also try to enter this function should be blocked (mutex, sleep, etc.). Are you saying just return from the function and let the other thread continue anyway?

Also when you say use a global variable, do you mean an atomic global? I'm open to doing whatever you want, because maybe I'm overthinking it for just a debug case anyway.

Thanks again!

Updated by jhawthorn (John Hawthorn) over 1 year ago Actions #4

  • Assignee set to ractor

Updated by hsbt (Hiroshi SHIBATA) over 1 year ago Actions #5

  • Status changed from Open to Assigned

Updated by ko1 (Koichi Sasada) 1 day ago Actions #6

  • Status changed from Assigned to Closed

Applied in changeset git|844ffa9007f7ae67e5f14bbf8ffe7ca190e1d772.


Let only the first thread write a bug report

When two Ractors fail an assertion at nearly the same time both write into
the same stream, and the first report -- the interesting one -- is cut
short by the second:

rs = 100.times.map { Ractor.new { sleep rand(1..3); Ractor.fail_assert } }

produced a 25-line report ending in "Crashed while printing bug report",
every time.

Take a claim before writing. The first thread through writes its report
and aborts the process; a later one waits for that instead of writing its
own. The claiming thread is let through again, so the existing
crash-while-reporting path still works. The wait is bounded, so a writer
that hangs still ends the process as a crash rather than a hang.

rb_assert_failure_detail() writes a report without going through
report_bug(), so it needs the same claim.

The example above now produces one complete report.

[Bug #21146]

Co-Authored-By: Claude Opus 5 (1M context)

Actions

Also available in: PDF Atom