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>
This commit is contained in:
Jeremy Evans 2021-04-07 11:32:30 -07:00
Родитель cbecf9c7ba
Коммит 87b327efe6
2 изменённых файлов: 29 добавлений и 0 удалений

4
gc.c
Просмотреть файл

@ -4088,10 +4088,14 @@ static void
finalize_deferred(rb_objspace_t *objspace) finalize_deferred(rb_objspace_t *objspace)
{ {
VALUE zombie; VALUE zombie;
rb_execution_context_t *ec = GET_EC();
ec->interrupt_mask |= PENDING_INTERRUPT_MASK;
while ((zombie = ATOMIC_VALUE_EXCHANGE(heap_pages_deferred_final, 0)) != 0) { while ((zombie = ATOMIC_VALUE_EXCHANGE(heap_pages_deferred_final, 0)) != 0) {
finalize_list(objspace, zombie); finalize_list(objspace, zombie);
} }
ec->interrupt_mask &= ~PENDING_INTERRUPT_MASK;
} }
static void static void

Просмотреть файл

@ -168,6 +168,31 @@ End
end; end;
end end
def test_finalizer_thread_raise
GC.disable
fzer = proc do |id|
sleep 0.2
end
2.times do
o = Object.new
ObjectSpace.define_finalizer(o, fzer)
end
my_error = Class.new(RuntimeError)
begin
main_th = Thread.current
Thread.new do
sleep 0.1
main_th.raise(my_error)
end
GC.start
puts "After GC"
sleep(10)
assert(false)
rescue my_error
end
end
def test_each_object def test_each_object
klass = Class.new klass = Class.new
new_obj = klass.new new_obj = klass.new