* 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
This commit is contained in:
parent
34ed81ea89
commit
833cade2dc
35
ChangeLog
35
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>
|
Wed May 5 22:22:51 2010 wanabe <s.wanabe@gmail.com>
|
||||||
|
|
||||||
* compile.c (iseq_set_sequence): fix check range of ic_index.
|
* compile.c (iseq_set_sequence): fix check range of ic_index.
|
||||||
|
@ -1161,3 +1161,17 @@ assert_equal 'ok', %q{
|
|||||||
"hello"[0, 1] ||= "H"
|
"hello"[0, 1] ||= "H"
|
||||||
"ok"
|
"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
|
||||||
|
}
|
||||||
|
1
cont.c
1
cont.c
@ -771,6 +771,7 @@ fiber_init(VALUE fibval, VALUE proc)
|
|||||||
th->cfp->iseq = 0;
|
th->cfp->iseq = 0;
|
||||||
th->cfp->proc = 0;
|
th->cfp->proc = 0;
|
||||||
th->cfp->block_iseq = 0;
|
th->cfp->block_iseq = 0;
|
||||||
|
th->cfp->me = 0;
|
||||||
th->tag = 0;
|
th->tag = 0;
|
||||||
th->local_storage = st_init_numtable();
|
th->local_storage = st_init_numtable();
|
||||||
|
|
||||||
|
5
gc.c
5
gc.c
@ -2205,6 +2205,11 @@ garbage_collect(rb_objspace_t *objspace)
|
|||||||
gc_sweep(objspace);
|
gc_sweep(objspace);
|
||||||
GC_PROF_SWEEP_TIMER_STOP;
|
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;
|
GC_PROF_TIMER_STOP;
|
||||||
if (GC_NOTIFY) printf("end garbage_collect()\n");
|
if (GC_NOTIFY) printf("end garbage_collect()\n");
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
7
method.h
7
method.h
@ -74,11 +74,17 @@ typedef struct rb_method_definition_struct {
|
|||||||
|
|
||||||
typedef struct rb_method_entry_struct {
|
typedef struct rb_method_entry_struct {
|
||||||
rb_method_flag_t flag;
|
rb_method_flag_t flag;
|
||||||
|
char mark;
|
||||||
rb_method_definition_t *def;
|
rb_method_definition_t *def;
|
||||||
ID called_id;
|
ID called_id;
|
||||||
VALUE klass; /* should be mark */
|
VALUE klass; /* should be mark */
|
||||||
} rb_method_entry_t;
|
} 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)
|
#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);
|
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_mark_method_entry(const rb_method_entry_t *me);
|
||||||
void rb_free_method_entry(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 */
|
#endif /* METHOD_H */
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#define RUBY_VERSION "1.9.2"
|
#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_PATCHLEVEL -1
|
||||||
#define RUBY_BRANCH_NAME "trunk"
|
#define RUBY_BRANCH_NAME "trunk"
|
||||||
|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
#define RUBY_VERSION_TEENY 1
|
#define RUBY_VERSION_TEENY 1
|
||||||
#define RUBY_RELEASE_YEAR 2010
|
#define RUBY_RELEASE_YEAR 2010
|
||||||
#define RUBY_RELEASE_MONTH 5
|
#define RUBY_RELEASE_MONTH 5
|
||||||
#define RUBY_RELEASE_DAY 5
|
#define RUBY_RELEASE_DAY 6
|
||||||
|
|
||||||
#include "ruby/version.h"
|
#include "ruby/version.h"
|
||||||
|
|
||||||
|
6
vm.c
6
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) {
|
else if (BUILTIN_TYPE(block->iseq) != T_NODE) {
|
||||||
const rb_iseq_t *iseq = block->iseq;
|
const rb_iseq_t *iseq = block->iseq;
|
||||||
const rb_control_frame_t *cfp;
|
const rb_control_frame_t *cfp;
|
||||||
|
rb_control_frame_t *ncfp;
|
||||||
int i, opt_pc, arg_size = iseq->arg_size;
|
int i, opt_pc, arg_size = iseq->arg_size;
|
||||||
int type = block_proc_is_lambda(block->proc) ?
|
int type = block_proc_is_lambda(block->proc) ?
|
||||||
VM_FRAME_MAGIC_LAMBDA : VM_FRAME_MAGIC_BLOCK;
|
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,
|
opt_pc = vm_yield_setup_args(th, iseq, argc, cfp->sp, blockptr,
|
||||||
type == VM_FRAME_MAGIC_LAMBDA);
|
type == VM_FRAME_MAGIC_LAMBDA);
|
||||||
|
|
||||||
vm_push_frame(th, iseq, type,
|
ncfp = vm_push_frame(th, iseq, type,
|
||||||
self, GC_GUARDED_PTR(block->dfp),
|
self, GC_GUARDED_PTR(block->dfp),
|
||||||
iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, block->lfp,
|
iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, block->lfp,
|
||||||
iseq->local_size - arg_size);
|
iseq->local_size - arg_size);
|
||||||
|
ncfp->me = th->passed_me;
|
||||||
|
th->passed_me = 0;
|
||||||
|
|
||||||
if (cref) {
|
if (cref) {
|
||||||
th->cfp->dfp[-1] = (VALUE)cref;
|
th->cfp->dfp[-1] = (VALUE)cref;
|
||||||
@ -1636,6 +1639,7 @@ rb_thread_mark(void *ptr)
|
|||||||
while (cfp != limit_cfp) {
|
while (cfp != limit_cfp) {
|
||||||
rb_gc_mark(cfp->proc);
|
rb_gc_mark(cfp->proc);
|
||||||
if (cfp->iseq) rb_gc_mark(cfp->iseq->self);
|
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);
|
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,6 +307,8 @@ typedef struct rb_vm_struct {
|
|||||||
VALUE verbose, debug, progname;
|
VALUE verbose, debug, progname;
|
||||||
VALUE coverages;
|
VALUE coverages;
|
||||||
|
|
||||||
|
struct unlinked_method_entry_list_entry *unlinked_method_entry_list;
|
||||||
|
|
||||||
#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
|
#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
|
||||||
struct rb_objspace *objspace;
|
struct rb_objspace *objspace;
|
||||||
#endif
|
#endif
|
||||||
@ -387,6 +389,9 @@ typedef struct rb_thread_struct
|
|||||||
/* for rb_iterate */
|
/* for rb_iterate */
|
||||||
const rb_block_t *passed_block;
|
const rb_block_t *passed_block;
|
||||||
|
|
||||||
|
/* for bmethod */
|
||||||
|
const rb_method_entry_t *passed_me;
|
||||||
|
|
||||||
/* for load(true) */
|
/* for load(true) */
|
||||||
VALUE top_self;
|
VALUE top_self;
|
||||||
VALUE top_wrapper;
|
VALUE top_wrapper;
|
||||||
|
@ -59,6 +59,7 @@ vm_push_frame(rb_thread_t * th, const rb_iseq_t * iseq,
|
|||||||
cfp->lfp = lfp;
|
cfp->lfp = lfp;
|
||||||
cfp->dfp = sp;
|
cfp->dfp = sp;
|
||||||
cfp->proc = 0;
|
cfp->proc = 0;
|
||||||
|
cfp->me = 0;
|
||||||
|
|
||||||
#define COLLECT_PROFILE 0
|
#define COLLECT_PROFILE 0
|
||||||
#if COLLECT_PROFILE
|
#if COLLECT_PROFILE
|
||||||
@ -419,7 +420,7 @@ vm_call_bmethod(rb_thread_t *th, VALUE recv, int argc, const VALUE *argv,
|
|||||||
VALUE val;
|
VALUE val;
|
||||||
|
|
||||||
/* control block frame */
|
/* control block frame */
|
||||||
(cfp-2)->me = me;
|
th->passed_me = me;
|
||||||
|
|
||||||
GetProcPtr(me->def->body.proc, proc);
|
GetProcPtr(me->def->body.proc, proc);
|
||||||
val = rb_vm_invoke_proc(th, proc, recv, argc, argv, blockptr);
|
val = rb_vm_invoke_proc(th, proc, recv, argc, argv, blockptr);
|
||||||
|
57
vm_method.c
57
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
|
void
|
||||||
rb_free_method_entry(rb_method_entry_t *me)
|
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
|
rb_unlink_method_entry(old_me);
|
||||||
* 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);
|
me = ALLOC(rb_method_entry_t);
|
||||||
}
|
|
||||||
|
|
||||||
rb_clear_cache_by_id(mid);
|
rb_clear_cache_by_id(mid);
|
||||||
|
|
||||||
me->flag = NOEX_WITH_SAFE(noex);
|
me->flag = NOEX_WITH_SAFE(noex);
|
||||||
|
me->mark = 0;
|
||||||
me->called_id = mid;
|
me->called_id = mid;
|
||||||
me->klass = klass;
|
me->klass = klass;
|
||||||
me->def = def;
|
me->def = def;
|
||||||
@ -453,7 +480,7 @@ remove_method(VALUE klass, ID mid)
|
|||||||
|
|
||||||
rb_vm_check_redefinition_opt_method(me);
|
rb_vm_check_redefinition_opt_method(me);
|
||||||
rb_clear_cache_for_undef(klass, mid);
|
rb_clear_cache_for_undef(klass, mid);
|
||||||
rb_free_method_entry(me);
|
rb_unlink_method_entry(me);
|
||||||
|
|
||||||
CALL_METHOD_HOOK(klass, removed, mid);
|
CALL_METHOD_HOOK(klass, removed, mid);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user