Project

General

Profile

Actions

Bug #15507

closed

Thread#raise is delivered to GC context

Added by larskanis (Lars Kanis) almost 3 years ago. Updated 4 months ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
ruby 2.6.0p0 (2018-12-25 revision 66547) [x86_64-linux]
[ruby-core:90898]
Tags:

Description

Since I started the development of Eventbox I noticed sporadic failures of the Eventbox test suite on MRI. One issue has been fixed in the meantime, but I was now able to minimize 3 others to a reproducible script. This is the first one:

This script combines a GC finalizer with Thread#raise:

Thread.abort_on_exception = true

class Finalizer
  def self.define
    ObjectSpace.define_finalizer(String.new, self.method(:gc))
  end

  def self.gc(object_id)
    puts "gc #{object_id} #{caller[0]}"
  end
end

class Deadlocker
  class Stop < RuntimeError
  end

  def self.run
    th = Thread.handle_interrupt(Exception => :never) do
      Thread.new do
        begin
          Thread.handle_interrupt(Stop => :on_blocking) do
            100.times { String.new } # Trigger GC
            sleep 5                  # Wait for Stop exception
            raise "not interrupted"
          end
        rescue Stop
        end
      end
    end

    th.raise Stop
    th.join
  end
end

100.times do
  Finalizer.define   # This alone works well
  Deadlocker.run     # This alone works equally, but both interfere badly
end

The program output looks similar to:

$ ruby -d --disable-gems interrupt-in-gc-error.rb
Exception `Deadlocker::Stop' at interrupt-in-gc-error.rb:23 - Deadlocker::Stop
Exception `Deadlocker::Stop' at interrupt-in-gc-error.rb:23 - Deadlocker::Stop
[...]
Exception `Deadlocker::Stop' at interrupt-in-gc-error.rb:9 - Deadlocker::Stop
gc 47133824012760 interrupt-in-gc-error.rb:22:in `call'
gc 47133824022800 interrupt-in-gc-error.rb:22:in `call'
[...]
Exception `RuntimeError' at interrupt-in-gc-error.rb:24 - not interrupted
#<Thread:0x000055bc65ac8f38@interrupt-in-gc-error.rb:19 run> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
    2: from interrupt-in-gc-error.rb:21:in `block (2 levels) in run'
    1: from interrupt-in-gc-error.rb:21:in `handle_interrupt'
interrupt-in-gc-error.rb:24:in `block (3 levels) in run': not interrupted (RuntimeError)

The debug output shows, that the Stop exception is delivered several times, so that the sleep call is properly interrupted. But if the interrupt is sent just in the moment when the finalizer is active, it is discarded and doesn't abort the sleep call.

IMHO interrupts shouldn't be delivered to any GC/trap context. At least they should be masked in this context.

This issue is present on all older MRI versions. However it doesn't appear on JRuby-9.2.5.0. So they seem to have solved the GC/interrupt relationship somehow.


Related issues

Is duplicate of Ruby master - Bug #13876: Tempfile's finalizer can be interrupted by a Timeout exception which can cause the process to hangClosedActions

Updated by larskanis (Lars Kanis) almost 3 years ago

The other two issues are #15508 and #15509 .

Actions #2

Updated by jeremyevans0 (Jeremy Evans) 7 months ago

  • Is duplicate of Bug #13876: Tempfile's finalizer can be interrupted by a Timeout exception which can cause the process to hang added

Updated by jeremyevans0 (Jeremy Evans) 7 months ago

I've confirmed this issue would be fixed by https://github.com/ruby/ruby/pull/4366.

Actions #4

Updated by jeremyevans (Jeremy Evans) 4 months ago

  • Status changed from Open to Closed

Applied in changeset git|87b327efe6c1f456c43b3f105c7a92a5a7effe93.


Do not check pending interrupts when running finalizers

This fixes cases where exceptions raised using Thread#raise are
swallowed by finalizers and not delivered to the running thread.

This could cause issues with finalizers that rely on pending interrupts,
but that case is expected to be rarer.

Fixes [Bug #13876]
Fixes [Bug #15507]

Co-authored-by: Koichi Sasada ko1@atdot.net

Actions

Also available in: Atom PDF