Permalink
Browse files

Make autoload thread safe

The strategy here is to reuse the fact that require is already thread
safe. This means we keep the autoload around and when a constant is
defined, we add it to the autoload itself. Only the currently loading
thread will see that constant for code loading.

Other threads will still see the Autoload and try to load the file, this
blocking until the loading is done and continue afterwards.
  • Loading branch information...
1 parent 249dca1 commit 1c0ccd98f16997c78295ca28784727f8807818d8 @dbussink dbussink committed May 17, 2013
@@ -4,7 +4,7 @@ module Rubinius
class ConstantTable
class Bucket
attr_reader :name
- attr_reader :constant
+ attr_accessor :constant
attr_accessor :visibility
attr_reader :next
@@ -12,11 +12,14 @@ def self.allocate
attr_reader :name
attr_reader :scope
attr_reader :path
+ attr_reader :constant
+ attr_reader :thread
def initialize(name, scope, path)
@name = name
@scope = scope
@path = path
+ @constant = undefined
end
##
@@ -35,10 +38,15 @@ def call(under, honor_require=false)
# be held by require until @path is available, at which time they'll
# attempt the lookup again.
#
- worked = resolve
- if !honor_require or worked
- find_const under
+ if !constant.equal?(undefined) && Thread.current == thread
+ constant
+ else
+ worked = resolve
+
+ if !honor_require or worked
+ find_const under
+ end
end
end
@@ -53,8 +61,13 @@ def find_const under
if entry = current.constant_table.lookup(name)
constant = entry.constant
if constant.equal? self
- unless Object.constant_table.lookup(name)
- return under.const_missing(name)
+ if constant.constant.equal? undefined
+ unless Object.constant_table.lookup(name)
+ return under.const_missing(name)
+ end
+ else
+ entry.constant = constant.constant
+ return constant.constant
end
end
return constant
@@ -67,7 +80,12 @@ def find_const under
if entry = Object.constant_table.lookup(name)
constant = entry.constant
if constant.equal? self
- return under.const_missing(name)
+ if constant.constant.equal? undefined
+ return under.const_missing(name)
+ else
+ entry.constant = constant.constant
+ return constant.constant
+ end
end
return constant
end
@@ -32,7 +32,7 @@ namespace rubinius {
if(CBOOL(res) || !honor_require) {
ConstantMissingReason reason = vNonExistent;
- Object* constant = Helpers::const_get_under(state, under, self->name(), &reason, self);
+ Object* constant = Helpers::const_get_under(state, under, self->name(), &reason, self, true);
if(!constant) return NULL;
@@ -53,7 +53,7 @@ namespace rubinius {
if(CBOOL(res) || !honor_require) {
ConstantMissingReason reason = vNonExistent;
- Object* constant = Helpers::const_get(state, call_frame, self->name(), &reason, self);
+ Object* constant = Helpers::const_get(state, call_frame, self->name(), &reason, self, true);
if(!constant) return NULL;
@@ -2,6 +2,7 @@
#define RBX_BUILTIN_AUTOLOAD_HPP
#include "builtin/object.hpp"
+#include "builtin/thread.hpp"
#include "type_info.hpp"
namespace rubinius {
@@ -15,11 +16,15 @@ namespace rubinius {
Symbol* name_; // slot
Module* scope_; // slot
Object* path_; // slot
+ Object* constant_; // slot
+ Thread* thread_; // slot
public:
attr_accessor(name, Symbol);
attr_accessor(scope, Module);
attr_accessor(path, Object);
+ attr_accessor(constant, Object);
+ attr_accessor(thread, Thread);
/** Register class with the VM. */
static void init(STATE);
@@ -10,6 +10,8 @@
#include "builtin/string.hpp"
#include "builtin/alias.hpp"
#include "builtin/constant_table.hpp"
+#include "builtin/autoload.hpp"
+#include "builtin/thread.hpp"
#include "on_stack.hpp"
@@ -120,7 +122,12 @@ namespace rubinius {
while(entry) {
if(entry->name() == name) {
- entry->constant(state, constant);
+ if(Autoload* autoload = try_as<Autoload>(entry->constant())) {
+ autoload->constant(state, constant);
+ autoload->thread(state, Thread::current(state));
+ } else {
+ entry->constant(state, constant);
+ }
return name;
}
View
@@ -6,6 +6,7 @@
#include "builtin/module.hpp"
#include "builtin/fixnum.hpp"
#include "builtin/constant_table.hpp"
+#include "builtin/autoload.hpp"
#include "builtin/methodtable.hpp"
#include "builtin/symbol.hpp"
#include "builtin/string.hpp"
@@ -129,7 +130,7 @@ namespace rubinius {
set_const(state, state->symbol(name), val);
}
- Object* Module::get_const(STATE, Symbol* sym, Symbol* min_vis, ConstantMissingReason* reason, bool check_super) {
+ Object* Module::get_const(STATE, Symbol* sym, Symbol* min_vis, ConstantMissingReason* reason, bool check_super, bool replace_autoload) {
Module* mod = this;
*reason = vNonExistent;
@@ -143,6 +144,15 @@ namespace rubinius {
break;
} else {
*reason = vFound;
+ if(Autoload* autoload = try_as<Autoload>(bucket->constant())) {
+ if(autoload->constant() != G(undefined)) {
+ if(replace_autoload) {
+ bucket->constant(state, autoload->constant());
+ } else if (autoload->thread() == Thread::current(state)) {
+ return autoload->constant();
+ }
+ }
+ }
return bucket->constant();
}
}
@@ -78,7 +78,7 @@ namespace rubinius {
void set_const(STATE, Symbol* sym, Object* val);
void set_const(STATE, std::string name, Object* val);
Object* get_const(STATE, Symbol* sym);
- Object* get_const(STATE, Symbol* sym, Symbol* min_vis, ConstantMissingReason* reason, bool check_super=false);
+ Object* get_const(STATE, Symbol* sym, Symbol* min_vis, ConstantMissingReason* reason, bool check_super=false, bool replace_autoload=false);
Object* get_const(STATE, std::string sym);
void del_const(STATE, Symbol* sym);
View
@@ -29,11 +29,11 @@
namespace rubinius {
namespace Helpers {
- Object* const_get_under(STATE, Module* mod, Symbol* name, ConstantMissingReason* reason, Object* filter) {
+ Object* const_get_under(STATE, Module* mod, Symbol* name, ConstantMissingReason* reason, Object* filter, bool replace_autoload) {
*reason = vNonExistent;
while(!mod->nil_p()) {
- Object* result = mod->get_const(state, name, G(sym_public), reason);
+ Object* result = mod->get_const(state, name, G(sym_public), reason, false, replace_autoload);
if(*reason == vFound) {
if(result != filter) return result;
*reason = vNonExistent;
@@ -49,7 +49,7 @@ namespace rubinius {
return cNil;
}
- Object* const_get(STATE, CallFrame* call_frame, Symbol* name, ConstantMissingReason* reason, Object* filter) {
+ Object* const_get(STATE, CallFrame* call_frame, Symbol* name, ConstantMissingReason* reason, Object* filter, bool replace_autoload) {
ConstantScope *cur;
Object* result;
@@ -100,7 +100,7 @@ namespace rubinius {
// Detect the toplevel scope (the default) and get outta dodge.
if(cur->top_level_p(state)) break;
- result = cur->module()->get_const(state, name, G(sym_private), reason);
+ result = cur->module()->get_const(state, name, G(sym_private), reason, false, replace_autoload);
if(*reason == vFound) {
if(result != filter) return result;
*reason = vNonExistent;
@@ -124,7 +124,7 @@ namespace rubinius {
fallback = NULL;
}
- result = mod->get_const(state, name, G(sym_private), reason);
+ result = mod->get_const(state, name, G(sym_private), reason, false, replace_autoload);
if(*reason == vFound) {
if(result != filter) return result;
*reason = vNonExistent;
@@ -136,7 +136,7 @@ namespace rubinius {
// Lastly, check the fallback scope (=Object) specifically if needed
if(fallback) {
- result = fallback->get_const(state, name, G(sym_private), reason, true);
+ result = fallback->get_const(state, name, G(sym_private), reason, true, replace_autoload);
if(*reason == vFound) {
if(result != filter) return result;
*reason = vNonExistent;
View
@@ -12,8 +12,8 @@ namespace rubinius {
class Tuple;
namespace Helpers {
- Object* const_get_under(State*, Module* under, Symbol* name, ConstantMissingReason* reason, Object* filter = NULL);
- Object* const_get(State*, CallFrame* call_frame, Symbol* name, ConstantMissingReason* reason, Object* filter = NULL);
+ Object* const_get_under(State*, Module* under, Symbol* name, ConstantMissingReason* reason, Object* filter = NULL, bool replace_autoload = false);
+ Object* const_get(State*, CallFrame* call_frame, Symbol* name, ConstantMissingReason* reason, Object* filter = NULL, bool replace_autoload = false);
Object* const_missing_under(State*, Module* under, Symbol* sym, CallFrame* call_frame);
Object* const_missing(State*, Symbol* sym, CallFrame* call_frame);
void const_set(State*, CallFrame* call_frame, Symbol* name, Object* val);

0 comments on commit 1c0ccd9

Please sign in to comment.