summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-05-05 17:51:21 +0000
committerko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-05-05 17:51:21 +0000
commit833cade2dce8ee8a9dd2091fcc84880030a51d54 (patch)
tree4e6e8437899658e77c877fa3e783e76322e34a9c
parent34ed81ea8948d8095389d38a4818c99b25155183 (diff)
* 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27634 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog35
-rw-r--r--bootstraptest/test_method.rb14
-rw-r--r--cont.c1
-rw-r--r--gc.c5
-rw-r--r--method.h7
-rw-r--r--version.h4
-rw-r--r--vm.c12
-rw-r--r--vm_core.h5
-rw-r--r--vm_insnhelper.c3
-rw-r--r--vm_method.c59
10 files changed, 122 insertions, 23 deletions
diff --git a/ChangeLog b/ChangeLog
index 542b7a0fee..0dc39f10e0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,38 @@
+Thu May 6 02:16:48 2010 Koichi Sasada <ko1@atdot.net>
+
+ * 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@gmail.com>
* compile.c (iseq_set_sequence): fix check range of ic_index.
diff --git a/bootstraptest/test_method.rb b/bootstraptest/test_method.rb
index 2ce66f6a6a..f57bf6214e 100644
--- a/bootstraptest/test_method.rb
+++ b/bootstraptest/test_method.rb
@@ -1161,3 +1161,17 @@ assert_equal 'ok', %q{
"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
+}
diff --git a/cont.c b/cont.c
index 185d8122e4..134cf5d36c 100644
--- a/cont.c
+++ b/cont.c
@@ -771,6 +771,7 @@ fiber_init(VALUE fibval, VALUE proc)
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();
diff --git a/gc.c b/gc.c
index 283a5bcf28..b88450d0e6 100644
--- a/gc.c
+++ b/gc.c
@@ -2205,6 +2205,11 @@ garbage_collect(rb_objspace_t *objspace)
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;
diff --git a/method.h b/method.h
index 98b89f8cf7..e03f0d2357 100644
--- a/method.h
+++ b/method.h
@@ -74,11 +74,17 @@ typedef struct rb_method_definition_struct {
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 @@ int rb_method_entry_arity(const rb_method_entry_t *me);
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 */
diff --git a/version.h b/version.h
index 34256b280f..0de1439a0a 100644
--- a/version.h
+++ b/version.h
@@ -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"
diff --git a/vm.c b/vm.c
index 3a1e37f9ff..e7c9a33df5 100644
--- a/vm.c
+++ b/vm.c
@@ -528,6 +528,7 @@ invoke_block_from_c(rb_thread_t *th, const rb_block_t *block,
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 @@ invoke_block_from_c(rb_thread_t *th, const rb_block_t *block,
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 @@ rb_thread_mark(void *ptr)
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);
}
}
diff --git a/vm_core.h b/vm_core.h
index b5c1bf0ad9..2174613156 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -307,6 +307,8 @@ typedef struct rb_vm_struct {
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 @@ typedef struct rb_thread_struct
/* 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;
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 5051d00e1e..be084b4126 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -59,6 +59,7 @@ vm_push_frame(rb_thread_t * th, const rb_iseq_t * iseq,
cfp->lfp = lfp;
cfp->dfp = sp;
cfp->proc = 0;
+ cfp->me = 0;
#define COLLECT_PROFILE 0
#if COLLECT_PROFILE
@@ -419,7 +420,7 @@ vm_call_bmethod(rb_thread_t *th, VALUE recv, int argc, const VALUE *argv,
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);
diff --git a/vm_method.c b/vm_method.c
index 4c3f16000a..69d50d969b 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -126,6 +126,44 @@ rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_me
}
}
+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)
{
@@ -214,26 +252,15 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
}
}
- /* 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;
- }
- }
- else {
- me = ALLOC(rb_method_entry_t);
+ rb_unlink_method_entry(old_me);
}
+ 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 @@ remove_method(VALUE klass, ID mid)
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);
}