[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]