ruby-changes:31166
From: nobu <ko1@a...>
Date: Fri, 11 Oct 2013 03:37:06 +0900 (JST)
Subject: [ruby-changes:31166] nobu:r43245 (trunk): vm_trace.c: fix infinite hook
nobu 2013-10-11 03:36:54 +0900 (Fri, 11 Oct 2013) New Revision: 43245 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=43245 Log: 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] Modified files: trunk/ChangeLog trunk/test/objspace/test_objspace.rb trunk/thread.c trunk/vm_trace.c Index: ChangeLog =================================================================== --- ChangeLog (revision 43244) +++ ChangeLog (revision 43245) @@ -1,3 +1,12 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Fri Oct 11 03:36:49 2013 Nobuyoshi Nakada <nobu@r...> + + * 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@g...> * array.c (rb_ary_or): remove unused variables. Index: thread.c =================================================================== --- thread.c (revision 43244) +++ thread.c (revision 43245) @@ -1924,29 +1924,33 @@ rb_threadptr_to_kill(rb_thread_t *th) https://github.com/ruby/ruby/blob/trunk/thread.c#L1924 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 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; - while (1) { - rb_atomic_t interrupt; - rb_atomic_t old; + while ((interrupt = threadptr_get_interrupts(th)) != 0) { int sig; int timer_interrupt; int pending_interrupt; - int postponed_job_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; pending_interrupt = interrupt & PENDING_INTERRUPT_MASK; postponed_job_interrupt = interrupt & POSTPONED_JOB_INTERRUPT_MASK; @@ -1984,10 +1988,6 @@ rb_threadptr_execute_interrupts(rb_threa https://github.com/ruby/ruby/blob/trunk/thread.c#L1988 } } - if (postponed_job_interrupt) { - rb_postponed_job_flush(th->vm); - } - if (timer_interrupt) { unsigned long limits_us = TIME_QUANTUM_USEC; @@ -2004,6 +2004,10 @@ rb_threadptr_execute_interrupts(rb_threa https://github.com/ruby/ruby/blob/trunk/thread.c#L2004 rb_thread_schedule_limits(limits_us); } } + + if (postponed_job_interrupt) { + rb_postponed_job_flush(th->vm); + } } void Index: vm_trace.c =================================================================== --- vm_trace.c (revision 43244) +++ vm_trace.c (revision 43245) @@ -1444,19 +1444,46 @@ rb_postponed_job_register_one(unsigned i https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L1444 void 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) { - int index = vm->postponed_job_index; + save.thread = cur_th; + save.interrupt_mask = cur_th->interrupt_mask; - if (index <= 0) { - return; /* finished */ - } + cur_th->interrupt_mask |= POSTPONED_JOB_INTERRUPT_MASK; + 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; - if (ATOMIC_CAS(vm->postponed_job_index, index, index-1) == index) { - pjob = &vm->postponed_job_buffer[index-1]; + 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; + save.old_index = old_index; + if (func) { /* do postponed job */ - pjob->func(pjob->data); + (*func)(data); } } + TH_POP_TAG(); + cur_th->interrupt_mask = save.interrupt_mask; } Index: test/objspace/test_objspace.rb =================================================================== --- test/objspace/test_objspace.rb (revision 43244) +++ test/objspace/test_objspace.rb (revision 43245) @@ -172,4 +172,23 @@ class TestObjSpace < Test::Unit::TestCas https://github.com/ruby/ruby/blob/trunk/test/objspace/test_objspace.rb#L172 assert_equal(nil, ObjectSpace.allocation_sourcefile(obj2)) assert_equal(nil, ObjectSpace.allocation_sourcefile(obj3)) 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 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/