vm_trace.c: fix infinite hook
* thread.c (rb_threadptr_execute_interrupts): flush postponed job only once at last. * vm_trace.c (rb_postponed_job_flush): defer calling postponed jobs registered while flushing to get rid of infinite reentrance of ObjectSpace.after_gc_start_hook. [ruby-dev:47400] [Bug #8492] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43245 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
abd6dc8c10
commit
53861b8acd
@ -1,3 +1,12 @@
|
|||||||
|
Fri Oct 11 03:36:49 2013 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
|
* thread.c (rb_threadptr_execute_interrupts): flush postponed job only
|
||||||
|
once at last.
|
||||||
|
|
||||||
|
* vm_trace.c (rb_postponed_job_flush): defer calling postponed jobs
|
||||||
|
registered while flushing to get rid of infinite reentrance of
|
||||||
|
ObjectSpace.after_gc_start_hook. [ruby-dev:47400] [Bug #8492]
|
||||||
|
|
||||||
Thu Oct 10 23:04:00 2013 Masaki Matsushita <glass.saga@gmail.com>
|
Thu Oct 10 23:04:00 2013 Masaki Matsushita <glass.saga@gmail.com>
|
||||||
|
|
||||||
* array.c (rb_ary_or): remove unused variables.
|
* array.c (rb_ary_or): remove unused variables.
|
||||||
|
@ -172,4 +172,23 @@ class TestObjSpace < Test::Unit::TestCase
|
|||||||
assert_equal(nil, ObjectSpace.allocation_sourcefile(obj2))
|
assert_equal(nil, ObjectSpace.allocation_sourcefile(obj2))
|
||||||
assert_equal(nil, ObjectSpace.allocation_sourcefile(obj3))
|
assert_equal(nil, ObjectSpace.allocation_sourcefile(obj3))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_after_gc_start_hook_with_GC_stress
|
||||||
|
bug8492 = '[ruby-dev:47400] [Bug #8492]: infinite after_gc_start_hook reentrance'
|
||||||
|
assert_nothing_raised(Timeout::Error, bug8492) do
|
||||||
|
assert_in_out_err(%w[-robjspace], <<-'end;', /\A[1-9]/, timeout: 2)
|
||||||
|
stress, GC.stress = GC.stress, false
|
||||||
|
count = 0
|
||||||
|
ObjectSpace.after_gc_start_hook = proc {count += 1}
|
||||||
|
begin
|
||||||
|
GC.stress = true
|
||||||
|
3.times {Object.new}
|
||||||
|
ensure
|
||||||
|
GC.stress = stress
|
||||||
|
ObjectSpace.after_gc_start_hook = nil
|
||||||
|
end
|
||||||
|
puts count
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
38
thread.c
38
thread.c
@ -1924,29 +1924,33 @@ rb_threadptr_to_kill(rb_thread_t *th)
|
|||||||
TH_JUMP_TAG(th, TAG_FATAL);
|
TH_JUMP_TAG(th, TAG_FATAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline rb_atomic_t
|
||||||
|
threadptr_get_interrupts(rb_thread_t *th)
|
||||||
|
{
|
||||||
|
rb_atomic_t interrupt;
|
||||||
|
rb_atomic_t old;
|
||||||
|
|
||||||
|
do {
|
||||||
|
interrupt = th->interrupt_flag;
|
||||||
|
old = ATOMIC_CAS(th->interrupt_flag, interrupt, interrupt & th->interrupt_mask);
|
||||||
|
} while (old != interrupt);
|
||||||
|
return interrupt & (rb_atomic_t)~th->interrupt_mask;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
|
rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
|
||||||
{
|
{
|
||||||
|
rb_atomic_t interrupt;
|
||||||
|
int postponed_job_interrupt = 0;
|
||||||
|
|
||||||
if (th->raised_flag) return;
|
if (th->raised_flag) return;
|
||||||
|
|
||||||
while (1) {
|
while ((interrupt = threadptr_get_interrupts(th)) != 0) {
|
||||||
rb_atomic_t interrupt;
|
|
||||||
rb_atomic_t old;
|
|
||||||
int sig;
|
int sig;
|
||||||
int timer_interrupt;
|
int timer_interrupt;
|
||||||
int pending_interrupt;
|
int pending_interrupt;
|
||||||
int postponed_job_interrupt;
|
|
||||||
int trap_interrupt;
|
int trap_interrupt;
|
||||||
|
|
||||||
do {
|
|
||||||
interrupt = th->interrupt_flag;
|
|
||||||
old = ATOMIC_CAS(th->interrupt_flag, interrupt, interrupt & th->interrupt_mask);
|
|
||||||
} while (old != interrupt);
|
|
||||||
|
|
||||||
interrupt &= (rb_atomic_t)~th->interrupt_mask;
|
|
||||||
if (!interrupt)
|
|
||||||
return;
|
|
||||||
|
|
||||||
timer_interrupt = interrupt & TIMER_INTERRUPT_MASK;
|
timer_interrupt = interrupt & TIMER_INTERRUPT_MASK;
|
||||||
pending_interrupt = interrupt & PENDING_INTERRUPT_MASK;
|
pending_interrupt = interrupt & PENDING_INTERRUPT_MASK;
|
||||||
postponed_job_interrupt = interrupt & POSTPONED_JOB_INTERRUPT_MASK;
|
postponed_job_interrupt = interrupt & POSTPONED_JOB_INTERRUPT_MASK;
|
||||||
@ -1984,10 +1988,6 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postponed_job_interrupt) {
|
|
||||||
rb_postponed_job_flush(th->vm);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timer_interrupt) {
|
if (timer_interrupt) {
|
||||||
unsigned long limits_us = TIME_QUANTUM_USEC;
|
unsigned long limits_us = TIME_QUANTUM_USEC;
|
||||||
|
|
||||||
@ -2004,6 +2004,10 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
|
|||||||
rb_thread_schedule_limits(limits_us);
|
rb_thread_schedule_limits(limits_us);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (postponed_job_interrupt) {
|
||||||
|
rb_postponed_job_flush(th->vm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
45
vm_trace.c
45
vm_trace.c
@ -1444,19 +1444,46 @@ rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func,
|
|||||||
void
|
void
|
||||||
rb_postponed_job_flush(rb_vm_t *vm)
|
rb_postponed_job_flush(rb_vm_t *vm)
|
||||||
{
|
{
|
||||||
rb_postponed_job_t *pjob;
|
rb_thread_t *cur_th = GET_THREAD();
|
||||||
|
volatile struct {
|
||||||
|
rb_thread_t *thread;
|
||||||
|
unsigned long interrupt_mask;
|
||||||
|
int index, old_index;
|
||||||
|
} save;
|
||||||
|
int index = vm->postponed_job_index, old_index = index;
|
||||||
|
|
||||||
while (1) {
|
save.thread = cur_th;
|
||||||
int index = vm->postponed_job_index;
|
save.interrupt_mask = cur_th->interrupt_mask;
|
||||||
|
|
||||||
if (index <= 0) {
|
cur_th->interrupt_mask |= POSTPONED_JOB_INTERRUPT_MASK;
|
||||||
return; /* finished */
|
TH_PUSH_TAG(cur_th);
|
||||||
|
if (EXEC_TAG()) {
|
||||||
|
/* ignore all jumps, just continue */
|
||||||
|
cur_th = save.thread;
|
||||||
|
index = save.index;
|
||||||
|
old_index = save.old_index;
|
||||||
|
}
|
||||||
|
while (index > 0) {
|
||||||
|
rb_postponed_job_t *pjob = &vm->postponed_job_buffer[--index];
|
||||||
|
void *data = pjob->data;
|
||||||
|
rb_postponed_job_func_t func = pjob->func;
|
||||||
|
|
||||||
|
pjob->func = 0; /* not to execute again */
|
||||||
|
if (old_index > 0) {
|
||||||
|
if (ATOMIC_CAS(vm->postponed_job_index, old_index, index) == old_index) {
|
||||||
|
old_index = index;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
old_index = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
save.index = index;
|
||||||
if (ATOMIC_CAS(vm->postponed_job_index, index, index-1) == index) {
|
save.old_index = old_index;
|
||||||
pjob = &vm->postponed_job_buffer[index-1];
|
if (func) {
|
||||||
/* do postponed job */
|
/* do postponed job */
|
||||||
pjob->func(pjob->data);
|
(*func)(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TH_POP_TAG();
|
||||||
|
cur_th->interrupt_mask = save.interrupt_mask;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user