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

ruby-changes:15712

From: ko1 <ko1@a...>
Date: Thu, 6 May 2010 02:51:41 +0900 (JST)
Subject: [ruby-changes:15712] Ruby:r27634 (trunk): * vm_method.c (rb_unlink_method_entry, rb_sweep_method_entry):

ko1	2010-05-06 02:51:21 +0900 (Thu, 06 May 2010)

  New Revision: 27634

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=27634

  Log:
    * vm_method.c (rb_unlink_method_entry, rb_sweep_method_entry):
      added.  Unlinked method entries are collected to
      vm->unlinked_method_entry_list.  On the GC timing, mark all method
      entries which are on all living threads.  Only non-marked method
      entries are collected.  This hack prevents releasing living method
      entry.
      [Performance Consideration] Since this Method Entry GC (MEGC)
      doesn't occuer frequently, MEGC will not be a performance bottleneck.
      However, to traverse living method entries, every control frame push
      needs to clear cfp->me field.  This will be a performance issue
      (because pushing control frame is occurred frequently).
      Bug #2777 [ruby-dev:40457]
    * cont.c (fiber_init): init cfp->me.
    * gc.c (garbage_collect): kick rb_sweep_method_entry().
    * method.h (rb_method_entry_t): add a mark field.
    * vm.c (invoke_block_from_c): set passed me.
    * vm.c (rb_thread_mark): mark cfp->me.
    * vm_core.h (rb_thread_t): add a field passed_me.
    * vm_core.h (rb_vm_t): add a field unlinked_method_entry_list.
    * vm_insnhelper.c (vm_push_frame): clear cfp->me at all times.
    * vm_insnhelper.c (vm_call_bmethod): pass me.
    * bootstraptest/test_method.rb: add a test.

  Modified files:
    trunk/ChangeLog
    trunk/bootstraptest/test_method.rb
    trunk/cont.c
    trunk/gc.c
    trunk/method.h
    trunk/version.h
    trunk/vm.c
    trunk/vm_core.h
    trunk/vm_insnhelper.c
    trunk/vm_method.c

Index: method.h
===================================================================
--- method.h	(revision 27633)
+++ method.h	(revision 27634)
@@ -74,11 +74,17 @@
 
 typedef struct rb_method_entry_struct {
     rb_method_flag_t flag;
+    char mark;
     rb_method_definition_t *def;
     ID called_id;
     VALUE klass;                    /* should be mark */
 } rb_method_entry_t;
 
+struct unlinked_method_entry_list_entry {
+    struct unlinked_method_entry_list_entry *next;
+    rb_method_entry_t *me;
+};
+
 #define UNDEFINED_METHOD_ENTRY_P(me) (!(me) || !(me)->def || (me)->def->type == VM_METHOD_TYPE_UNDEF)
 
 void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_flag_t noex);
@@ -92,5 +98,6 @@
 
 void rb_mark_method_entry(const rb_method_entry_t *me);
 void rb_free_method_entry(rb_method_entry_t *me);
+void rb_sweep_method_entry(void *vm);
 
 #endif /* METHOD_H */
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 27633)
+++ ChangeLog	(revision 27634)
@@ -1,3 +1,38 @@
+Thu May  6 02:16:48 2010  Koichi Sasada  <ko1@a...>
+
+	* vm_method.c (rb_unlink_method_entry, rb_sweep_method_entry):
+	  added.  Unlinked method entries are collected to
+	  vm->unlinked_method_entry_list.  On the GC timing, mark all method
+	  entries which are on all living threads.  Only non-marked method
+	  entries are collected.  This hack prevents releasing living method
+	  entry.
+	  [Performance Consideration] Since this Method Entry GC (MEGC)
+	  doesn't occuer frequently, MEGC will not be a performance bottleneck.
+	  However, to traverse living method entries, every control frame push
+	  needs to clear cfp->me field.  This will be a performance issue
+	  (because pushing control frame is occurred frequently).
+	  Bug #2777 [ruby-dev:40457]
+
+	* cont.c (fiber_init): init cfp->me.
+
+	* gc.c (garbage_collect): kick rb_sweep_method_entry().
+
+	* method.h (rb_method_entry_t): add a mark field.
+
+	* vm.c (invoke_block_from_c): set passed me.
+
+	* vm.c (rb_thread_mark): mark cfp->me.
+
+	* vm_core.h (rb_thread_t): add a field passed_me.
+
+	* vm_core.h (rb_vm_t): add a field unlinked_method_entry_list.
+
+	* vm_insnhelper.c (vm_push_frame): clear cfp->me at all times.
+
+	* vm_insnhelper.c (vm_call_bmethod): pass me.
+
+	* bootstraptest/test_method.rb: add a test.
+
 Wed May  5 22:22:51 2010  wanabe  <s.wanabe@g...>
 
 	* compile.c (iseq_set_sequence): fix check range of ic_index.
Index: bootstraptest/test_method.rb
===================================================================
--- bootstraptest/test_method.rb	(revision 27633)
+++ bootstraptest/test_method.rb	(revision 27634)
@@ -1161,3 +1161,17 @@
   "hello"[0, 1] ||= "H"
   "ok"
 }
+
+assert_equal 'ok', %q{
+  class C
+    define_method(:foo) do
+      C.class_eval { remove_method(:foo) }
+      super()
+    end
+  end
+  begin
+    C.new.foo
+  rescue NoMethodError
+    'ok'
+  end
+}
Index: vm_core.h
===================================================================
--- vm_core.h	(revision 27633)
+++ vm_core.h	(revision 27634)
@@ -307,6 +307,8 @@
     VALUE verbose, debug, progname;
     VALUE coverages;
 
+    struct unlinked_method_entry_list_entry *unlinked_method_entry_list;
+
 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
     struct rb_objspace *objspace;
 #endif
@@ -387,6 +389,9 @@
     /* for rb_iterate */
     const rb_block_t *passed_block;
 
+    /* for bmethod */
+    const rb_method_entry_t *passed_me;
+
     /* for load(true) */
     VALUE top_self;
     VALUE top_wrapper;
Index: vm_method.c
===================================================================
--- vm_method.c	(revision 27633)
+++ vm_method.c	(revision 27634)
@@ -126,7 +126,45 @@
     }
 }
 
+static void
+rb_unlink_method_entry(rb_method_entry_t *me)
+{
+    struct unlinked_method_entry_list_entry *ume = ALLOC(struct unlinked_method_entry_list_entry);
+    ume->me = me;
+    ume->next = GET_VM()->unlinked_method_entry_list;
+    GET_VM()->unlinked_method_entry_list = ume;
+}
+
 void
+rb_sweep_method_entry(void *pvm)
+{
+    rb_vm_t *vm = pvm;
+    struct unlinked_method_entry_list_entry *ume = vm->unlinked_method_entry_list, *prev_ume = 0, *curr_ume;
+
+    while (ume) {
+	if (ume->me->mark) {
+	    ume->me->mark = 0;
+	    prev_ume = ume;
+	    ume = ume->next;
+	}
+	else {
+	    rb_free_method_entry(ume->me);
+
+	    if (prev_ume == 0) {
+		vm->unlinked_method_entry_list = ume->next;
+	    }
+	    else {
+		prev_ume->next = ume->next;
+	    }
+
+	    curr_ume = ume;
+	    ume = ume->next;
+	    xfree(curr_ume);
+	}
+    }
+}
+
+void
 rb_free_method_entry(rb_method_entry_t *me)
 {
     rb_method_definition_t *def = me->def;
@@ -214,26 +252,15 @@
 	    }
 	}
 
-	/* FIXME: this avoid to free methods used in cfp, but reusing may cause
-	 * another problem when the usage is changed.
-	 */
-	me = old_me;
-
-	if (me->def) {
-	    if (me->def->alias_count == 0)
-		xfree(me->def);
-	    else if (me->def->alias_count > 0)
-		me->def->alias_count--;
-	    me->def = 0;
-	}
+	rb_unlink_method_entry(old_me);
     }
-    else {
-	me = ALLOC(rb_method_entry_t);
-    }
 
+    me = ALLOC(rb_method_entry_t);
+
     rb_clear_cache_by_id(mid);
 
     me->flag = NOEX_WITH_SAFE(noex);
+    me->mark = 0;
     me->called_id = mid;
     me->klass = klass;
     me->def = def;
@@ -453,7 +480,7 @@
 
     rb_vm_check_redefinition_opt_method(me);
     rb_clear_cache_for_undef(klass, mid);
-    rb_free_method_entry(me);
+    rb_unlink_method_entry(me);
 
     CALL_METHOD_HOOK(klass, removed, mid);
 }
Index: gc.c
===================================================================
--- gc.c	(revision 27633)
+++ gc.c	(revision 27634)
@@ -2205,6 +2205,11 @@
     gc_sweep(objspace);
     GC_PROF_SWEEP_TIMER_STOP;
 
+    /* sweep unlinked method entries */
+    if (th->vm->unlinked_method_entry_list) {
+	rb_sweep_method_entry(th->vm);
+    }
+
     GC_PROF_TIMER_STOP;
     if (GC_NOTIFY) printf("end garbage_collect()\n");
     return TRUE;
Index: cont.c
===================================================================
--- cont.c	(revision 27633)
+++ cont.c	(revision 27634)
@@ -771,6 +771,7 @@
     th->cfp->iseq = 0;
     th->cfp->proc = 0;
     th->cfp->block_iseq = 0;
+    th->cfp->me = 0;
     th->tag = 0;
     th->local_storage = st_init_numtable();
 
Index: vm.c
===================================================================
--- vm.c	(revision 27633)
+++ vm.c	(revision 27634)
@@ -528,6 +528,7 @@
     else if (BUILTIN_TYPE(block->iseq) != T_NODE) {
 	const rb_iseq_t *iseq = block->iseq;
 	const rb_control_frame_t *cfp;
+	rb_control_frame_t *ncfp;
 	int i, opt_pc, arg_size = iseq->arg_size;
 	int type = block_proc_is_lambda(block->proc) ?
 	  VM_FRAME_MAGIC_LAMBDA : VM_FRAME_MAGIC_BLOCK;
@@ -544,10 +545,12 @@
 	opt_pc = vm_yield_setup_args(th, iseq, argc, cfp->sp, blockptr,
 				     type == VM_FRAME_MAGIC_LAMBDA);
 
-	vm_push_frame(th, iseq, type,
-		      self, GC_GUARDED_PTR(block->dfp),
-		      iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, block->lfp,
-		      iseq->local_size - arg_size);
+	ncfp = vm_push_frame(th, iseq, type,
+			     self, GC_GUARDED_PTR(block->dfp),
+			     iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, block->lfp,
+			     iseq->local_size - arg_size);
+	ncfp->me = th->passed_me;
+	th->passed_me = 0;
 
 	if (cref) {
 	    th->cfp->dfp[-1] = (VALUE)cref;
@@ -1636,6 +1639,7 @@
 	    while (cfp != limit_cfp) {
 		rb_gc_mark(cfp->proc);
 		if (cfp->iseq) rb_gc_mark(cfp->iseq->self);
+		if (cfp->me) ((rb_method_entry_t *)cfp->me)->mark = 1;
 		cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
 	    }
 	}
Index: version.h
===================================================================
--- version.h	(revision 27633)
+++ version.h	(revision 27634)
@@ -1,5 +1,5 @@
 #define RUBY_VERSION "1.9.2"
-#define RUBY_RELEASE_DATE "2010-05-05"
+#define RUBY_RELEASE_DATE "2010-05-06"
 #define RUBY_PATCHLEVEL -1
 #define RUBY_BRANCH_NAME "trunk"
 
@@ -8,7 +8,7 @@
 #define RUBY_VERSION_TEENY 1
 #define RUBY_RELEASE_YEAR 2010
 #define RUBY_RELEASE_MONTH 5
-#define RUBY_RELEASE_DAY 5
+#define RUBY_RELEASE_DAY 6
 
 #include "ruby/version.h"
 
Index: vm_insnhelper.c
===================================================================
--- vm_insnhelper.c	(revision 27633)
+++ vm_insnhelper.c	(revision 27634)
@@ -59,6 +59,7 @@
     cfp->lfp = lfp;
     cfp->dfp = sp;
     cfp->proc = 0;
+    cfp->me = 0;
 
 #define COLLECT_PROFILE 0
 #if COLLECT_PROFILE
@@ -419,7 +420,7 @@
     VALUE val;
 
     /* control block frame */
-    (cfp-2)->me = me;
+    th->passed_me = me;
 
     GetProcPtr(me->def->body.proc, proc);
     val = rb_vm_invoke_proc(th, proc, recv, argc, argv, blockptr);

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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