Permalink
Browse files

Add a concurrent Module#autoload spec.

  • Loading branch information...
1 parent f76d3f6 commit 987a3276f99e5a4870ea4fe4255469dbfb800cfd @thedarkone committed Dec 9, 2012
View
47 spec/ruby/core/module/autoload_spec.rb
@@ -418,6 +418,53 @@ class ModuleSpecs::Autoload::Z < ModuleSpecs::Autoload::ZZ
t2_exc.should be_nil
end
end
+
+ ruby_version_is "2.0" do
+ describe "(concurrently)" do
+ it "blocks others threads while doing an autoload" do
+ file_path = fixture(__FILE__, "repeated_concurrent_autoload.rb")
+ autoload_path = file_path.sub /\.rb\Z/, ''
+ mod_count = 30
+ thread_count = 16
+
+ mod_names = []
+ mod_count.times do |i|
+ mod_name = :"Mod#{i}"
+ autoload mod_name, autoload_path
+ mod_names << mod_name
+ end
+
+ barrier = ModuleSpecs::CyclicBarrier.new thread_count
+ ScratchPad.record ModuleSpecs::ThreadSafeCounter.new
+
+ threads = (1..thread_count).map do
+ Thread.new do
+ mod_names.each do |mod_name|
+ break false unless barrier.enabled?
+
+ was_last_one_in = barrier.await # wait for all threads to finish the iteration
+ # clean up so we can autoload the same file again
+ $LOADED_FEATURES.delete(file_path) if was_last_one_in && $LOADED_FEATURES.include?(file_path)
+ barrier.await # get ready for race
+
+ begin
+ Object.const_get(mod_name).foo
+ rescue NoMethodError
+ barrier.disable!
+ break false
+ end
+ end
+ end
+ end
+
+ # check that no thread got a NoMethodError because of partially loaded module
+ threads.all? {|t| t.value}.should be_true
+
+ # check that the autloaded file was evaled exactly once
+ ScratchPad.recorded.get.should == mod_count
+ end
+ end
+ end
end
describe "Module#autoload" do
View
54 spec/ruby/core/module/fixtures/classes.rb
@@ -403,6 +403,60 @@ def extend_object(obj)
private :extend_object
end
end
+
+ require 'thread' # get Mutex class
+ class CyclicBarrier
+ def initialize(count = 1)
+ @count = count
+ @state = 0
+ @mutex = Mutex.new
+ @cond = ConditionVariable.new
+ end
+
+ def await
+ @mutex.synchronize do
+ @state += 1
+ if @state >= @count
+ @state = 0
+ @cond.broadcast
+ true
+ else
+ @cond.wait @mutex
+ false
+ end
+ end
+ end
+
+ def enabled?
+ @mutex.synchronize { @count != -1 }
+ end
+
+ def disable!
+ @mutex.synchronize do
+ @count = -1
+ @cond.broadcast
+ end
+ end
+ end
+
+ class ThreadSafeCounter
+ def initialize(value = 0)
+ @value = 0
+ @mutex = Mutex.new
+ end
+
+ def get
+ @mutex.synchronize { @value }
+ end
+
+ def increment_and_get
+ @mutex.synchronize do
+ prev_value = @value
+ @value += 1
+ prev_value
+ end
+ end
+ end
end
class Object
View
8 spec/ruby/core/module/fixtures/repeated_concurrent_autoload.rb
@@ -0,0 +1,8 @@
+prev_value = ScratchPad.recorded.increment_and_get
+eval <<-RUBY_EVAL
+ module Mod#{prev_value}
+ sleep(0.05)
+ def self.foo
+ end
+ end
+RUBY_EVAL

0 comments on commit 987a327

Please sign in to comment.