Skip to content
This repository
Browse code

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.
  • Loading branch information...
commit 719b008f1dae30c5fb6d09a371ae8d949c867a0c 1 parent 685192b
pinetops authored May 21, 2012
28  actionpack/lib/action_view/template/resolver.rb
@@ -27,6 +27,16 @@ def initialize(name, prefix, partial, virtual)
27 27
 
28 28
     # Threadsafe template cache
29 29
     class Cache #:nodoc:
  30
+      class CacheEntry
  31
+        attr_accessor :templates
  32
+
  33
+        delegate :synchronize, :to => "@mutex"
  34
+
  35
+        def initialize
  36
+          @mutex = Mutex.new
  37
+        end
  38
+      end
  39
+
30 40
       def initialize
31 41
         @data = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2|
32 42
                   h2[k2] = Hash.new { |h3,k3| h3[k3] = Hash.new { |h4,k4| h4[k4] = {} } } } }
@@ -35,24 +45,32 @@ def initialize
35 45
 
36 46
       # Cache the templates returned by the block
37 47
       def cache(key, name, prefix, partial, locals)
  48
+        cache_entry = nil
  49
+
  50
+        # first obtain a lock on the main data structure to create the cache entry
38 51
         @mutex.synchronize do
  52
+          cache_entry = @data[key][name][prefix][partial][locals] ||= CacheEntry.new
  53
+        end
  54
+
  55
+        # then to avoid a long lasting global lock, obtain a more granular lock
  56
+        # on the CacheEntry itself
  57
+        cache_entry.synchronize do
39 58
           if Resolver.caching?
40 59
             # all templates are cached forever the first time they are accessed
41  
-            @data[key][name][prefix][partial][locals] ||= yield
  60
+            cache_entry.templates ||= yield
42 61
           else
43 62
             # templates are still cached, but are only returned if they are
44 63
             # all still current
45 64
             fresh = yield
46 65
 
47  
-            cache = @data[key][name][prefix][partial][locals]
48  
-            mtime = cache && cache.map(&:updated_at).max
  66
+            mtime = cache_entry.templates && cache_entry.templates.map(&:updated_at).max
49 67
 
50 68
             newer = !mtime || fresh.empty?  || fresh.any? { |t| t.updated_at > mtime }
51 69
 
52 70
             if newer
53  
-              @data[key][name][prefix][partial][locals] = fresh
  71
+              cache_entry.templates = fresh
54 72
             else
55  
-              @data[key][name][prefix][partial][locals]
  73
+              cache_entry.templates
56 74
             end
57 75
           end
58 76
         end

0 notes on commit 719b008

Please sign in to comment.
Something went wrong with that request. Please try again.