aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
authorTom Clarke <tom@u2i.com>2012-05-21 09:41:04 -0400
committerTom Clarke <tom@u2i.com>2012-05-21 15:24:15 -0400
commit63f3393f88a160d5bf87c84d7317a16379587280 (patch)
treec1321e2d5d84977ae0a0b84a6a1ba8337d2512e3 /actionpack
parent2a121870a6aba2b40d65e75d8fa4f7b959aac2c9 (diff)
downloadrails-63f3393f88a160d5bf87c84d7317a16379587280.tar.gz
rails-63f3393f88a160d5bf87c84d7317a16379587280.tar.bz2
rails-63f3393f88a160d5bf87c84d7317a16379587280.zip
More granular locking of the Resolver template cache
In order to avoid holding a global lock when doing template resolution, instead add individual locks on a per cache entry basis. The global lock is now only used for manipulation of the main cache data structure.
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/lib/action_view/template/resolver.rb28
1 files changed, 23 insertions, 5 deletions
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index b33aebd3bc..8f87d6da8b 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -27,6 +27,16 @@ module ActionView
# Threadsafe template cache
class Cache #:nodoc:
+ class CacheEntry
+ attr_accessor :templates
+
+ delegate :synchronize, :to => "@mutex"
+
+ def initialize
+ @mutex = Mutex.new
+ end
+ end
+
def initialize
@data = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2|
h2[k2] = Hash.new { |h3,k3| h3[k3] = Hash.new { |h4,k4| h4[k4] = {} } } } }
@@ -35,24 +45,32 @@ module ActionView
# Cache the templates returned by the block
def cache(key, name, prefix, partial, locals)
+ cache_entry = nil
+
+ # first obtain a lock on the main data structure to create the cache entry
@mutex.synchronize do
+ cache_entry = @data[key][name][prefix][partial][locals] ||= CacheEntry.new
+ end
+
+ # then to avoid a long lasting global lock, obtain a more granular lock
+ # on the CacheEntry itself
+ cache_entry.synchronize do
if Resolver.caching?
# all templates are cached forever the first time they are accessed
- @data[key][name][prefix][partial][locals] ||= yield
+ cache_entry.templates ||= yield
else
# templates are still cached, but are only returned if they are
# all still current
fresh = yield
- cache = @data[key][name][prefix][partial][locals]
- mtime = cache && cache.map(&:updated_at).max
+ mtime = cache_entry.templates && cache_entry.templates.map(&:updated_at).max
newer = !mtime || fresh.empty? || fresh.any? { |t| t.updated_at > mtime }
if newer
- @data[key][name][prefix][partial][locals] = fresh
+ cache_entry.templates = fresh
else
- @data[key][name][prefix][partial][locals]
+ cache_entry.templates
end
end
end