From 719b008f1dae30c5fb6d09a371ae8d949c867a0c Mon Sep 17 00:00:00 2001 From: Tom Clarke Date: Mon, 21 May 2012 09:41:04 -0400 Subject: 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. --- actionpack/lib/action_view/template/resolver.rb | 28 ++++++++++++++++++++----- 1 file 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 -- cgit v1.2.3