Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
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.
commit 1c0ccd98f16997c78295ca28784727f8807818d8 1 parent 249dca1
@dbussink dbussink authored
View
2  kernel/bootstrap/constant_table.rb
@@ -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
View
30 kernel/common/autoload.rb
@@ -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
View
4 vm/builtin/autoload.cpp
@@ -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;
View
5 vm/builtin/autoload.hpp
@@ -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);
View
9 vm/builtin/constant_table.cpp
@@ -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
12 vm/builtin/module.cpp
@@ -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();
}
}
View
2  vm/builtin/module.hpp
@@ -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
12 vm/helpers.cpp
@@ -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
4 vm/helpers.hpp
@@ -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);
Please sign in to comment.
Something went wrong with that request. Please try again.