1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

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.
This commit is contained in:
Tom Clarke 2012-05-21 09:41:04 -04:00
parent 2a121870a6
commit 63f3393f88

View file

@ -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