summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/node/environment.rb20
-rw-r--r--lib/puppet/parser/compiler.rb5
-rwxr-xr-xspec/unit/node/environment_spec.rb35
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|