check interrupts at each frame pop timing.
Asynchronous events such as signal trap, finalization timing, thread switching and so on are managed by "interrupt_flag". Ruby's threads check this flag periodically and if a thread does not check this flag, above events doesn't happen. This checking is CHECK_INTS() (related) macro and it is placed at some places (laeve instruction and so on). However, at the end of C methods, C blocks (IMEMO_IFUNC) etc there are no checking and it can introduce uninterruptible thread. To modify this situation, we decide to place CHECK_INTS() at vm_pop_frame(). It increases interrupt checking points. [Bug #16366] This patch can introduce unexpected events...
This commit is contained in:
parent
c4686b9235
commit
36da0b3da1
3
enum.c
3
enum.c
@ -543,7 +543,6 @@ collect_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
|
|||||||
static VALUE
|
static VALUE
|
||||||
collect_all(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
|
collect_all(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
|
||||||
{
|
{
|
||||||
rb_thread_check_ints();
|
|
||||||
rb_ary_push(ary, rb_enum_values_pack(argc, argv));
|
rb_ary_push(ary, rb_enum_values_pack(argc, argv));
|
||||||
|
|
||||||
return Qnil;
|
return Qnil;
|
||||||
@ -663,14 +662,12 @@ static VALUE
|
|||||||
enum_to_h_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
enum_to_h_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
||||||
{
|
{
|
||||||
ENUM_WANT_SVALUE();
|
ENUM_WANT_SVALUE();
|
||||||
rb_thread_check_ints();
|
|
||||||
return rb_hash_set_pair(hash, i);
|
return rb_hash_set_pair(hash, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
enum_to_h_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
enum_to_h_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
||||||
{
|
{
|
||||||
rb_thread_check_ints();
|
|
||||||
return rb_hash_set_pair(hash, rb_yield_values2(argc, argv));
|
return rb_hash_set_pair(hash, rb_yield_values2(argc, argv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,28 @@
|
|||||||
#include "ruby.h"
|
#include "ruby.h"
|
||||||
#include "ruby/debug.h"
|
#include "ruby/debug.h"
|
||||||
|
|
||||||
|
static int counter;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pjob_callback(void *data)
|
pjob_callback(void *data)
|
||||||
{
|
{
|
||||||
VALUE ary = (VALUE)data;
|
VALUE ary = (VALUE)data;
|
||||||
Check_Type(ary, T_ARRAY);
|
Check_Type(ary, T_ARRAY);
|
||||||
|
|
||||||
rb_ary_replace(ary, rb_funcall(Qnil, rb_intern("caller"), 0));
|
rb_ary_push(ary, INT2FIX(counter));
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
pjob_register(VALUE self, VALUE obj)
|
pjob_register(VALUE self, VALUE obj)
|
||||||
{
|
{
|
||||||
|
counter = 0;
|
||||||
rb_postponed_job_register(0, pjob_callback, (void *)obj);
|
rb_postponed_job_register(0, pjob_callback, (void *)obj);
|
||||||
|
rb_gc_start();
|
||||||
|
counter++;
|
||||||
|
rb_gc_start();
|
||||||
|
counter++;
|
||||||
|
rb_gc_start();
|
||||||
|
counter++;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +47,14 @@ pjob_register_one(VALUE self, VALUE obj)
|
|||||||
static VALUE
|
static VALUE
|
||||||
pjob_call_direct(VALUE self, VALUE obj)
|
pjob_call_direct(VALUE self, VALUE obj)
|
||||||
{
|
{
|
||||||
|
counter = 0;
|
||||||
pjob_callback((void *)obj);
|
pjob_callback((void *)obj);
|
||||||
|
rb_gc_start();
|
||||||
|
counter++;
|
||||||
|
rb_gc_start();
|
||||||
|
counter++;
|
||||||
|
rb_gc_start();
|
||||||
|
counter++;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -935,8 +935,6 @@ leave
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RUBY_VM_CHECK_INTS(ec);
|
|
||||||
|
|
||||||
if (vm_pop_frame(ec, GET_CFP(), GET_EP())) {
|
if (vm_pop_frame(ec, GET_CFP(), GET_EP())) {
|
||||||
#if OPT_CALL_THREADED_CODE
|
#if OPT_CALL_THREADED_CODE
|
||||||
rb_ec_thread_ptr(ec)->retval = val;
|
rb_ec_thread_ptr(ec)->retval = val;
|
||||||
@ -963,7 +961,6 @@ throw
|
|||||||
/* Same discussion as leave. */
|
/* Same discussion as leave. */
|
||||||
// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
|
// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
|
||||||
{
|
{
|
||||||
RUBY_VM_CHECK_INTS(ec);
|
|
||||||
val = vm_throw(ec, GET_CFP(), throw_state, throwobj);
|
val = vm_throw(ec, GET_CFP(), throw_state, throwobj);
|
||||||
THROW_EXCEPTION(val);
|
THROW_EXCEPTION(val);
|
||||||
/* unreachable */
|
/* unreachable */
|
||||||
|
@ -138,11 +138,14 @@ describe "Thread#raise on a running thread" do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "can go unhandled" do
|
it "can go unhandled" do
|
||||||
|
q = Queue.new
|
||||||
t = Thread.new do
|
t = Thread.new do
|
||||||
Thread.current.report_on_exception = false
|
Thread.current.report_on_exception = false
|
||||||
|
q << true
|
||||||
loop { Thread.pass }
|
loop { Thread.pass }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
q.pop # wait for `report_on_exception = false`.
|
||||||
t.raise
|
t.raise
|
||||||
-> { t.value }.should raise_error(RuntimeError)
|
-> { t.value }.should raise_error(RuntimeError)
|
||||||
end
|
end
|
||||||
|
@ -19,8 +19,8 @@ class TestPostponed_job < Test::Unit::TestCase
|
|||||||
Bug.postponed_job_call_direct_wrapper(direct)
|
Bug.postponed_job_call_direct_wrapper(direct)
|
||||||
Bug.postponed_job_register_wrapper(registered)
|
Bug.postponed_job_register_wrapper(registered)
|
||||||
|
|
||||||
assert_match( /postponed_job_call_direct_wrapper/, direct.join)
|
assert_equal([0], direct)
|
||||||
assert_not_match( /postponed_job_register_wrapper/, registered.join)
|
assert_equal([3], registered)
|
||||||
|
|
||||||
Bug.postponed_job_register_one(ary = [])
|
Bug.postponed_job_register_one(ary = [])
|
||||||
assert_equal [1], ary
|
assert_equal [1], ary
|
||||||
|
@ -816,17 +816,19 @@ class TestThread < Test::Unit::TestCase
|
|||||||
def test_handle_interrupt_and_io
|
def test_handle_interrupt_and_io
|
||||||
assert_in_out_err([], <<-INPUT, %w(ok), [])
|
assert_in_out_err([], <<-INPUT, %w(ok), [])
|
||||||
th_waiting = true
|
th_waiting = true
|
||||||
|
q = Queue.new
|
||||||
|
|
||||||
t = Thread.new {
|
t = Thread.new {
|
||||||
Thread.current.report_on_exception = false
|
Thread.current.report_on_exception = false
|
||||||
Thread.handle_interrupt(RuntimeError => :on_blocking) {
|
Thread.handle_interrupt(RuntimeError => :on_blocking) {
|
||||||
|
q << true
|
||||||
nil while th_waiting
|
nil while th_waiting
|
||||||
# async interrupt should be raised _before_ writing puts arguments
|
# async interrupt should be raised _before_ writing puts arguments
|
||||||
puts "ng"
|
puts "ng"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.pass while t.stop?
|
q.pop
|
||||||
t.raise RuntimeError
|
t.raise RuntimeError
|
||||||
th_waiting = false
|
th_waiting = false
|
||||||
t.join rescue nil
|
t.join rescue nil
|
||||||
|
@ -354,6 +354,7 @@ vm_pop_frame(rb_execution_context_t *ec, rb_control_frame_t *cfp, const VALUE *e
|
|||||||
if (VM_CHECK_MODE >= 4) rb_gc_verify_internal_consistency();
|
if (VM_CHECK_MODE >= 4) rb_gc_verify_internal_consistency();
|
||||||
if (VMDEBUG == 2) SDR();
|
if (VMDEBUG == 2) SDR();
|
||||||
|
|
||||||
|
RUBY_VM_CHECK_INTS(ec);
|
||||||
ec->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
ec->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
||||||
|
|
||||||
return flags & VM_FRAME_FLAG_FINISH;
|
return flags & VM_FRAME_FLAG_FINISH;
|
||||||
@ -2318,7 +2319,6 @@ vm_call_iseq_setup_tailcall(rb_execution_context_t *ec, rb_control_frame_t *cfp,
|
|||||||
iseq->body->stack_max);
|
iseq->body->stack_max);
|
||||||
|
|
||||||
cfp->sp = sp_orig;
|
cfp->sp = sp_orig;
|
||||||
RUBY_VM_CHECK_INTS(ec);
|
|
||||||
|
|
||||||
return Qundef;
|
return Qundef;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user