diff options
-rw-r--r-- | lib/puppet/node/environment.rb | 20 | ||||
-rw-r--r-- | lib/puppet/parser/compiler.rb | 5 | ||||
-rwxr-xr-x | spec/unit/node/environment_spec.rb | 35 |
3 files changed, 52 insertions, 8 deletions
diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 762599cff..3f67474f9 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -1,4 +1,5 @@ require 'puppet/util/cacher' +require 'monitor' # Just define it, so this class has fewer load dependencies. class Puppet::Node @@ -67,14 +68,23 @@ class Puppet::Node::Environment def initialize(name) @name = name + extend MonitorMixin end def known_resource_types - if @known_resource_types.nil? or @known_resource_types.stale? - @known_resource_types = Puppet::Resource::TypeCollection.new(self) - @known_resource_types.perform_initial_import - end - @known_resource_types + # This makes use of short circuit evaluation to get the right thread-safe + # per environment semantics with an efficient most common cases; we almost + # always just return our thread's known-resource types. Only at the start + # of a compilation (after our thread var has been set to nil) or when the + # environment has changed do we delve deeper. + Thread.current[:known_resource_types] = nil if (krt = Thread.current[:known_resource_types]) && krt.environment != self + Thread.current[:known_resource_types] ||= synchronize { + if @known_resource_types.nil? or @known_resource_types.stale? + @known_resource_types = Puppet::Resource::TypeCollection.new(self) + @known_resource_types.perform_initial_import + end + @known_resource_types + } end def module(name) diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index a901c0dd6..760d5a75a 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -15,6 +15,11 @@ class Puppet::Parser::Compiler include Puppet::Resource::TypeCollectionHelper def self.compile(node) + # At the start of a new compile we don't assume anything about + # known_resouce_types; we'll get these from the environment and + # cache them in a thread variable for the duration of the + # compilation. + Thread.current[:known_resource_types] = nil new(node).compile.to_resource rescue => detail puts detail.backtrace if Puppet[:trace] diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index b400865a2..6edcce56c 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -53,6 +53,7 @@ describe Puppet::Node::Environment do @env = Puppet::Node::Environment.new("dev") @collection = Puppet::Resource::TypeCollection.new(@env) @collection.stubs(:perform_initial_import) + Thread.current[:known_resource_types] = nil end it "should create a resource type collection if none exists" do @@ -71,13 +72,41 @@ describe Puppet::Node::Environment do @env.known_resource_types end - it "should create and return a new collection rather than returning a stale collection" do - @env.known_resource_types.expects(:stale?).returns true + it "should return the same collection even if stale if it's the same thread" do + Puppet::Resource::TypeCollection.stubs(:new).returns @collection + @env.known_resource_types.stubs(:stale?).returns true - Puppet::Resource::TypeCollection.expects(:new).returns @collection + @env.known_resource_types.should equal(@collection) + end + + it "should return the current thread associated collection if there is one" do + Thread.current[:known_resource_types] = @collection @env.known_resource_types.should equal(@collection) end + + it "should give to all threads the same collection if it didn't change" do + Puppet::Resource::TypeCollection.expects(:new).with(@env).returns @collection + @env.known_resource_types + + t = Thread.new { + @env.known_resource_types.should equal(@collection) + } + t.join + end + + it "should give to new threads a new collection if it isn't stale" do + Puppet::Resource::TypeCollection.expects(:new).with(@env).returns @collection + @env.known_resource_types.expects(:stale?).returns(true) + + Puppet::Resource::TypeCollection.expects(:new).returns @collection + + t = Thread.new { + @env.known_resource_types.should equal(@collection) + } + t.join + end + end [:modulepath, :manifestdir].each do |setting| |