aboutsummaryrefslogtreecommitdiffstats
path: root/vm_method.c
diff options
context:
space:
mode:
authorcharliesome <charliesome@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-09-04 05:25:06 +0000
committercharliesome <charliesome@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-09-04 05:25:06 +0000
commit2f522b9cc6f3e184404040b12af4486520a73b26 (patch)
tree7e24db4e9d97f1096442eadb272215340865336f /vm_method.c
parent4142e8301dd618a775f611bc7bf6c049ce6a4bf9 (diff)
downloadruby-2f522b9cc6f3e184404040b12af4486520a73b26.tar.gz
* 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.c105
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);
}
/*