Browse files

Use spinlocks over object lock in method table

We need to lock around reads of the method table as well, and with
object locks we need access to the call frame because it might GC in the
mean while. Changing this is tricky, since it also affects for example
the JIT which doesn't have these structures.

Therefore this switches it to a spinlock, because there are no GC paths
necessary in the method table and this way we can lock around it
properly for reads as well.
  • Loading branch information...
1 parent aa2c5ca commit ddd5079ac2e02c0395e739e5a4cc36ba3cf68789 @dbussink dbussink committed May 8, 2013
View
93 vm/builtin/methodtable.cpp
@@ -31,6 +31,7 @@ namespace rubinius {
tbl = state->new_object<MethodTable>(G(methtbl));
tbl->setup(state, size);
+ tbl->lock_.init();
return tbl;
}
@@ -46,13 +47,16 @@ namespace rubinius {
MethodTable* MethodTable::allocate(STATE, Object* self) {
MethodTable* tbl = create(state, METHODTABLE_MIN_SIZE);
tbl->klass(state, as<Class>(self));
+ tbl->lock_.init();
return tbl;
}
- MethodTable* MethodTable::duplicate(STATE, GCToken gct, CallFrame* call_frame) {
+ MethodTable* MethodTable::duplicate(STATE) {
size_t size, i;
MethodTable* dup = 0;
+ utilities::thread::SpinLock::LockGuard lg(lock_);
+
size = bins_->to_native();
dup = MethodTable::create(state, size);
@@ -61,16 +65,13 @@ namespace rubinius {
size_t num = bins_->to_native();
- MethodTable* self = this;
MethodTableBucket* entry = 0;
- OnStack<3> os(state, dup, self, entry);
-
for(i = 0; i < num; i++) {
- entry = try_as<MethodTableBucket>(self->values_->at(state, i));
+ entry = try_as<MethodTableBucket>(values_->at(state, i));
while(entry) {
- dup->store(state, gct, entry->name(), entry->method(), entry->visibility(), call_frame);
+ dup->store(state, entry->name(), entry->method(), entry->visibility());
entry = try_as<MethodTableBucket>(entry->next());
}
}
@@ -106,49 +107,45 @@ namespace rubinius {
bins(state, Fixnum::from(size));
}
- Object* MethodTable::store(STATE, GCToken gct, Symbol* name, Object* exec,
- Symbol* vis, CallFrame* calling_environment)
+ Object* MethodTable::store(STATE, Symbol* name, Object* exec, Symbol* vis)
{
check_frozen(state);
- MethodTable* self = this;
-
- OnStack<2> os(state, self, exec);
- hard_lock(state, gct, calling_environment);
+ utilities::thread::SpinLock::LockGuard lg(lock_);
Executable* method;
if(exec->nil_p()) {
method = nil<Executable>();
} else {
if(Alias* stored_alias = try_as<Alias>(exec)) {
- self->hard_unlock(state, gct, calling_environment);
- return self->alias(state, gct, name, vis,
+ lock_.unlock();
+ Object* res = alias(state, name, vis,
stored_alias->original_name(),
stored_alias->original_exec(),
- stored_alias->original_module(),
- calling_environment);
+ stored_alias->original_module());
+ lock_.lock();
+ return res;
} else {
method = as<Executable>(exec);
}
}
- native_int num_entries = self->entries_->to_native();
- native_int num_bins = self->bins_->to_native();
+ native_int num_entries = entries_->to_native();
+ native_int num_bins = bins_->to_native();
if(max_density_p(num_entries, num_bins)) {
- self->redistribute(state, num_bins <<= 1);
+ redistribute(state, num_bins <<= 1);
}
native_int bin = find_bin(key_hash(name), num_bins);
- MethodTableBucket* entry = try_as<MethodTableBucket>(self->values_->at(state, bin));
+ MethodTableBucket* entry = try_as<MethodTableBucket>(values_->at(state, bin));
MethodTableBucket* last = NULL;
while(entry) {
if(entry->name() == name) {
entry->method(state, method);
entry->visibility(state, vis);
- self->hard_unlock(state, gct, calling_environment);
return name;
}
@@ -159,26 +156,22 @@ namespace rubinius {
if(last) {
last->next(state, MethodTableBucket::create(state, name, method, vis));
} else {
- self->values_->put(state, bin,
+ values_->put(state, bin,
MethodTableBucket::create(state, name, method, vis));
}
- self->entries(state, Fixnum::from(num_entries + 1));
+ entries(state, Fixnum::from(num_entries + 1));
- self->hard_unlock(state, gct, calling_environment);
return name;
}
- Object* MethodTable::alias(STATE, GCToken gct, Symbol* name, Symbol* vis,
+ Object* MethodTable::alias(STATE, Symbol* name, Symbol* vis,
Symbol* orig_name, Object* orig_method,
- Module* orig_mod, CallFrame* calling_environment)
+ Module* orig_mod)
{
check_frozen(state);
- MethodTable* self = this;
-
- OnStack<3> os(state, self, orig_method, orig_mod);
- hard_lock(state, gct, calling_environment);
+ utilities::thread::SpinLock::LockGuard lg(lock_);
Executable* orig_exec;
@@ -194,23 +187,22 @@ namespace rubinius {
Alias* method = Alias::create(state, orig_name, orig_mod, orig_exec);
- native_int num_entries = self->entries_->to_native();
- native_int num_bins = self->bins_->to_native();
+ native_int num_entries = entries_->to_native();
+ native_int num_bins = bins_->to_native();
if(max_density_p(num_entries, num_bins)) {
- self->redistribute(state, num_bins <<= 1);
+ redistribute(state, num_bins <<= 1);
}
native_int bin = find_bin(key_hash(name), num_bins);
- MethodTableBucket* entry = try_as<MethodTableBucket>(self->values_->at(state, bin));
+ MethodTableBucket* entry = try_as<MethodTableBucket>(values_->at(state, bin));
MethodTableBucket* last = NULL;
while(entry) {
if(entry->name() == name) {
entry->method(state, method);
entry->visibility(state, vis);
- self->hard_unlock(state, gct, calling_environment);
return name;
}
@@ -221,18 +213,17 @@ namespace rubinius {
if(last) {
last->next(state, MethodTableBucket::create(state, name, method, vis));
} else {
- self->values_->put(state, bin,
+ values_->put(state, bin,
MethodTableBucket::create(state, name, method, vis));
}
- self->entries(state, Fixnum::from(num_entries + 1));
-
- self->hard_unlock(state, gct, calling_environment);
+ entries(state, Fixnum::from(num_entries + 1));
return name;
}
MethodTableBucket* MethodTable::find_entry(STATE, Symbol* name) {
unsigned int bin;
+ utilities::thread::SpinLock::LockGuard lg(lock_);
bin = find_bin(key_hash(name), bins_->to_native());
MethodTableBucket *entry = try_as<MethodTableBucket>(values_->at(state, bin));
@@ -249,6 +240,7 @@ namespace rubinius {
MethodTableBucket* MethodTable::find_entry(Symbol* name) {
unsigned int bin;
+ utilities::thread::SpinLock::LockGuard lg(lock_);
bin = find_bin(key_hash(name), bins_->to_native());
MethodTableBucket *entry = try_as<MethodTableBucket>(values_->at(bin));
@@ -271,25 +263,21 @@ namespace rubinius {
return nil<MethodTableBucket>();
}
- Executable* MethodTable::remove(STATE, GCToken gct, Symbol* name, CallFrame* calling_environment) {
+ Executable* MethodTable::remove(STATE, Symbol* name) {
check_frozen(state);
- MethodTable* self = this;
-
- OnStack<1> os(state, self);
+ utilities::thread::SpinLock::LockGuard lg(lock_);
- self->hard_lock(state, gct, calling_environment);
-
- native_int num_entries = self->entries_->to_native();
- native_int num_bins = self->bins_->to_native();
+ native_int num_entries = entries_->to_native();
+ native_int num_bins = bins_->to_native();
if(min_density_p(num_entries, num_bins) &&
(num_bins >> 1) >= METHODTABLE_MIN_SIZE) {
- self->redistribute(state, num_bins >>= 1);
+ redistribute(state, num_bins >>= 1);
}
native_int bin = find_bin(key_hash(name), num_bins);
- MethodTableBucket* entry = try_as<MethodTableBucket>(self->values_->at(state, bin));
+ MethodTableBucket* entry = try_as<MethodTableBucket>(values_->at(state, bin));
MethodTableBucket* last = NULL;
while(entry) {
@@ -298,20 +286,17 @@ namespace rubinius {
if(last) {
last->next(state, entry->next());
} else {
- self->values_->put(state, bin, entry->next());
+ values_->put(state, bin, entry->next());
}
- self->entries(state, Fixnum::from(entries_->to_native() - 1));
- self->hard_unlock(state, gct, calling_environment);
+ entries(state, Fixnum::from(entries_->to_native() - 1));
return val;
}
last = entry;
entry = try_as<MethodTableBucket>(entry->next());
}
- self->hard_unlock(state, gct, calling_environment);
-
return nil<Executable>();
}
View
9 vm/builtin/methodtable.hpp
@@ -48,6 +48,7 @@ namespace rubinius {
Tuple* values_; // slot
Integer* bins_; // slot
Integer* entries_; // slot
+ utilities::thread::SpinLock lock_;
void redistribute(STATE, size_t size);
@@ -67,13 +68,13 @@ namespace rubinius {
static MethodTable* allocate(STATE, Object* self);
// Rubinius.primitive :methodtable_store
- Object* store(STATE, GCToken gct, Symbol* name, Object* meth, Symbol* vis, CallFrame* calling_environment);
+ Object* store(STATE, Symbol* name, Object* meth, Symbol* vis);
// Rubinius.primitive :methodtable_alias
- Object* alias(STATE, GCToken gct, Symbol* name, Symbol* vis, Symbol* orig_name, Object* orig_method, Module* orig_mod, CallFrame* calling_environment);
+ Object* alias(STATE, Symbol* name, Symbol* vis, Symbol* orig_name, Object* orig_method, Module* orig_mod);
// Rubinius.primitive :methodtable_duplicate
- MethodTable* duplicate(STATE, GCToken gct, CallFrame* calling_environment);
+ MethodTable* duplicate(STATE);
MethodTableBucket* find_entry(STATE, Symbol* name);
MethodTableBucket* find_entry(Symbol* name);
@@ -82,7 +83,7 @@ namespace rubinius {
MethodTableBucket* lookup(STATE, Symbol* name);
// Rubinius.primitive :methodtable_delete
- Executable* remove(STATE, GCToken gct, Symbol* name, CallFrame* calling_environment);
+ Executable* remove(STATE, Symbol* name);
// Rubinius.primitive :methodtable_has_name
Object* has_name(STATE, Symbol* name);
View
10 vm/builtin/module.cpp
@@ -153,14 +153,10 @@ namespace rubinius {
return get_const(state, state->symbol(sym));
}
- void Module::add_method(STATE, GCToken gct, CallFrame* call_frame,
- Symbol* name, Executable* exec, Symbol* vis) {
- Module* self = this;
- OnStack<2> os(state, self, exec);
-
+ void Module::add_method(STATE, Symbol* name, Executable* exec, Symbol* vis) {
if(!vis) vis = G(sym_public);
- method_table_->store(state, gct, name, exec, vis, call_frame);
- state->vm()->global_cache()->clear(state, self, name);
+ method_table_->store(state, name, exec, vis);
+ state->vm()->global_cache()->clear(state, this, name);
reset_method_cache(state, name);
}
View
2 vm/builtin/module.hpp
@@ -87,7 +87,7 @@ namespace rubinius {
void del_const(STATE, Symbol* sym);
- void add_method(STATE, GCToken gct, CallFrame* call_frame, Symbol* name, Executable* exec, Symbol* vis = 0);
+ void add_method(STATE, Symbol* name, Executable* exec, Symbol* vis = 0);
Object* reset_method_cache(STATE, Symbol* name);
Executable* find_method(Symbol* name, Module** defined_in = 0);
View
2 vm/builtin/object.cpp
@@ -69,7 +69,7 @@ namespace rubinius {
OnStack<4> os(state, self, sc, source_methods, source_constants);
- source_methods = sc->method_table()->duplicate(state, gct, calling_environment);
+ source_methods = sc->method_table()->duplicate(state);
source_constants = sc->constant_table()->duplicate(state);
self->singleton_class(state)->method_table(state, source_methods);
View
14 vm/builtin/system.cpp
@@ -143,7 +143,7 @@ namespace rubinius {
oc->primitive(state, prim);
oc->resolve_primitive(state);
- tbl->store(state, gct, name, oc, G(sym_public), 0);
+ tbl->store(state, name, oc, G(sym_public));
}
/* Primitives */
@@ -972,13 +972,10 @@ namespace rubinius {
method->scope(state, scope);
method->serial(state, Fixnum::from(0));
- OnStack<4> os(state, mod, method, scope, vis);
- state->set_call_frame(calling_environment);
-
- mod->add_method(state, gct, calling_environment, name, method);
+ mod->add_method(state, name, method);
if(Class* cls = try_as<Class>(mod)) {
- OnStack<1> o2(state, cls);
+ OnStack<5> o2(state, mod, method, scope, vis, cls);
if(!method->internalize(state, gct, calling_environment)) {
Exception::argument_error(state, "invalid bytecode method");
@@ -1033,10 +1030,7 @@ namespace rubinius {
method->scope(state, scope);
method->serial(state, Fixnum::from(0));
- OnStack<2> os(state, mod, method);
-
- mod->add_method(state, gct, calling_environment, name, method);
-
+ mod->add_method(state, name, method);
vm_reset_method_cache(state, mod, name, calling_environment);
return method;
View
3 vm/capi/capi.cpp
@@ -700,8 +700,7 @@ extern "C" {
break;
}
- GCTokenImpl gct;
- module->add_method(state, gct, env->current_call_frame(), method_name, method, visibility);
+ module->add_method(state, method_name, method, visibility);
System::vm_reset_method_cache(env->state(), module, method_name, env->current_call_frame());
}
View
18 vm/on_stack.hpp
@@ -87,6 +87,24 @@ namespace rubinius {
objects_[2] = reinterpret_cast<Object**>(&o3);
objects_[3] = reinterpret_cast<Object**>(&o4);
}
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ OnStack(STATE, T1& o1, T2& o2, T3& o3, T4& o4, T5& o5)
+ : buffer_(state->vm()->current_root_buffers(), objects_, size)
+ {
+ (void)static_cast<Object*>(o1);
+ (void)static_cast<Object*>(o2);
+ (void)static_cast<Object*>(o3);
+ (void)static_cast<Object*>(o4);
+ (void)static_cast<Object*>(o5);
+
+ objects_[0] = reinterpret_cast<Object**>(&o1);
+ objects_[1] = reinterpret_cast<Object**>(&o2);
+ objects_[2] = reinterpret_cast<Object**>(&o3);
+ objects_[3] = reinterpret_cast<Object**>(&o4);
+ objects_[4] = reinterpret_cast<Object**>(&o4);
+ }
+
};
}

0 comments on commit ddd5079

Please sign in to comment.