From bb2b9269f2da9e26840440218587992e86bc6e53 Mon Sep 17 00:00:00 2001 From: Hiroshi Shirosaki Date: Wed, 11 Nov 2015 11:54:51 +0900 Subject: [PATCH] Add waiting for autoload in rb_const_defined_0 * variable.c (rb_const_defined_0): add waiting for autoload. defined? works properly when other thread is autoloading. * test/ruby/test_autoload.rb (test_defined_with_autoload): add a test for the above. --- test/ruby/test_autoload.rb | 22 ++++++++++++++++++++++ variable.c | 23 ++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb index a672e0b..c71668e 100644 --- a/test/ruby/test_autoload.rb +++ b/test/ruby/test_autoload.rb @@ -234,6 +234,28 @@ p Foo::Bar end end + def test_defined_with_autoload + ruby_impl_require do |called_with| + Tempfile.create(%w(autoload .rb)) do |file| + file.puts "class AutoloadTest; module B; end; end" + file.flush + add_autoload(file.path) + begin + thrs = [] + 2.times do + thrs << Thread.new do + Thread.pass; assert_equal("constant", defined? Object::AutoloadTest::B) + end + end + thrs.each(&:join) + ensure + remove_autoload_constant + end + assert_equal [file.path], called_with.uniq + end + end + end + def add_autoload(path) (@autoload_paths ||= []) << path ::Object.class_eval {autoload(:AutoloadTest, path)} diff --git a/variable.c b/variable.c index c8264b1..a01fbb6 100644 --- a/variable.c +++ b/variable.c @@ -2493,8 +2493,29 @@ rb_const_defined_0(VALUE klass, ID id, int exclude, int recurse, int visibility) return (int)Qfalse; } if (ce->value == Qundef && !check_autoload_required(tmp, id, 0) && - !rb_autoloading_value(tmp, id, 0)) + !rb_autoloading_value(tmp, id, 0)) { + VALUE load; + struct autoload_data_i *ele; + struct autoload_state state; + if (!(load = autoload_data(tmp, id)) || + !(ele = check_autoload_data(load))) { + return (int)Qfalse; + } + state.thread = rb_thread_current(); + if (ele->state && (ele->state->thread != state.thread)) { + list_add_tail(&ele->state->waitq.head, &state.waitq.node); + /* + * autoload_reset in other thread will resume us and remove us + * from the waitq list + */ + do { + rb_thread_sleep_deadly(); + } while (state.thread != Qfalse); + /* retry to look up the autoloaded constant. */ + continue; + } return (int)Qfalse; + } return (int)Qtrue; } if (!recurse) break; -- 2.5.0