diff options
author | charliesome <charliesome@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-09-04 05:25:06 +0000 |
---|---|---|
committer | charliesome <charliesome@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-09-04 05:25:06 +0000 |
commit | 2f522b9cc6f3e184404040b12af4486520a73b26 (patch) | |
tree | 7e24db4e9d97f1096442eadb272215340865336f /vm_method.c | |
parent | 4142e8301dd618a775f611bc7bf6c049ce6a4bf9 (diff) |
* class.c, compile.c, eval.c, gc.h, insns.def, internal.h, method.h,
variable.c, vm.c, vm_core.c, vm_insnhelper.c, vm_insnhelper.h,
vm_method.c: Implement class hierarchy method cache invalidation.
[ruby-core:55053] [Feature #8426] [GH-387]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@42822 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'vm_method.c')
-rw-r--r-- | vm_method.c | 105 |
1 files changed, 57 insertions, 48 deletions
diff --git a/vm_method.c b/vm_method.c index a026699acb..b8b3b579aa 100644 --- a/vm_method.c +++ b/vm_method.c @@ -2,9 +2,7 @@ * This file is included by vm.c */ -#define CACHE_SIZE 0x800 -#define CACHE_MASK 0x7ff -#define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK) +#include "method.h" #define NOEX_NOREDEF 0 #ifndef NOEX_NOREDEF @@ -22,53 +20,32 @@ static void rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me, VAL #define singleton_undefined idSingleton_method_undefined #define attached id__attached__ -struct cache_entry { /* method hash table. */ - VALUE filled_version; /* filled state version */ - ID mid; /* method's id */ - VALUE klass; /* receiver's class */ - rb_method_entry_t *me; - VALUE defined_class; -}; - -static struct cache_entry cache[CACHE_SIZE]; #define ruby_running (GET_VM()->running) /* int ruby_running = 0; */ static void -vm_clear_global_method_cache(void) +rb_class_clear_method_cache(VALUE klass) { - struct cache_entry *ent, *end; - - ent = cache; - end = ent + CACHE_SIZE; - while (ent < end) { - ent->filled_version = 0; - ent++; - } + RCLASS_EXT(klass)->seq = rb_next_class_sequence(); + rb_class_foreach_subclass(klass, rb_class_clear_method_cache); } void rb_clear_cache(void) { - rb_vm_change_state(); -} - -static void -rb_clear_cache_for_undef(VALUE klass, ID id) -{ - rb_vm_change_state(); -} - -static void -rb_clear_cache_by_id(ID id) -{ - rb_vm_change_state(); + INC_VM_STATE_VERSION(); } void rb_clear_cache_by_class(VALUE klass) { - rb_vm_change_state(); + if (klass && klass != Qundef) { + if (klass == rb_cBasicObject || klass == rb_cObject || klass == rb_mKernel) { + INC_VM_STATE_VERSION(); + } else { + rb_class_clear_method_cache(klass); + } + } } VALUE @@ -310,7 +287,7 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type, me = ALLOC(rb_method_entry_t); - rb_clear_cache_by_id(mid); + rb_clear_cache_by_class(klass); me->flag = NOEX_WITH_SAFE(noex); me->mark = 0; @@ -472,6 +449,7 @@ rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_ if (type != VM_METHOD_TYPE_UNDEF && type != VM_METHOD_TYPE_REFINED) { method_added(klass, mid); } + rb_clear_cache_by_class(klass); return me; } @@ -540,26 +518,26 @@ rb_method_entry_at(VALUE klass, ID id) */ rb_method_entry_t * rb_method_entry_get_without_cache(VALUE klass, ID id, - VALUE *defined_class_ptr) + VALUE *defined_class_ptr, + method_cache_entry_t *ent) { VALUE defined_class; rb_method_entry_t *me = search_method(klass, id, &defined_class); if (ruby_running) { - struct cache_entry *ent; - ent = cache + EXPR1(klass, id); - ent->filled_version = GET_VM_STATE_VERSION(); - ent->klass = klass; - ent->defined_class = defined_class; + ent->seq = RCLASS_EXT(klass)->seq; + ent->vm_state = GET_VM_STATE_VERSION(); if (UNDEFINED_METHOD_ENTRY_P(me)) { ent->mid = id; ent->me = 0; + ent->defined_class = defined_class; me = 0; } else { ent->mid = id; ent->me = me; + ent->defined_class = defined_class; } } @@ -568,22 +546,52 @@ rb_method_entry_get_without_cache(VALUE klass, ID id, return me; } +#if VM_DEBUG_VERIFY_METHOD_CACHE +static void +verify_method_cache(VALUE klass, ID id, VALUE defined_class, rb_method_entry_t *me) +{ + VALUE actual_defined_class; + method_cache_entry_t ent; + rb_method_entry_t *actual_me = + rb_method_entry_get_without_cache(klass, id, &actual_defined_class, &ent); + + if (me != actual_me || defined_class != actual_defined_class) { + rb_bug("method cache verification failed"); + } +} +#endif + rb_method_entry_t * rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr) { #if OPT_GLOBAL_METHOD_CACHE - struct cache_entry *ent; + method_cache_entry_t *ent; + + if (RCLASS_EXT(klass)->mc_tbl == NULL) { + RCLASS_EXT(klass)->mc_tbl = st_init_numtable(); + } - ent = cache + EXPR1(klass, id); - if (ent->filled_version == GET_VM_STATE_VERSION() && - ent->mid == id && ent->klass == klass) { + if (!st_lookup(RCLASS_EXT(klass)->mc_tbl, (st_index_t)id, (st_data_t *)&ent)) { + ent = calloc(1, sizeof(*ent)); + st_insert(RCLASS_EXT(klass)->mc_tbl, (st_index_t)id, (st_data_t)ent); + } + + if (ent->seq == RCLASS_EXT(klass)->seq && + ent->vm_state == GET_VM_STATE_VERSION() && + ent->mid == id) { if (defined_class_ptr) *defined_class_ptr = ent->defined_class; +#if VM_DEBUG_VERIFY_METHOD_CACHE + verify_method_cache(klass, id, ent->defined_class, ent->me); +#endif return ent->me; } +#else + method_cache_entry_t ent_; + method_cache_entry_t* ent = &ent_; #endif - return rb_method_entry_get_without_cache(klass, id, defined_class_ptr); + return rb_method_entry_get_without_cache(klass, id, defined_class_ptr, ent); } static rb_method_entry_t * @@ -687,7 +695,7 @@ remove_method(VALUE klass, ID mid) st_delete(RCLASS_M_TBL(klass), &key, &data); rb_vm_check_redefinition_opt_method(me, klass); - rb_clear_cache_for_undef(klass, mid); + rb_clear_cache_by_class(klass); rb_unlink_method_entry(me); CALL_METHOD_HOOK(self, removed, mid); @@ -1220,6 +1228,7 @@ rb_alias(VALUE klass, ID name, ID def) if (flag == NOEX_UNDEF) flag = orig_me->flag; rb_method_entry_set(target_klass, name, orig_me, flag); + rb_clear_cache_by_class(target_klass); } /* |