public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
MemoryStore is the only "unsafe" store. Make it threadsafe by default.
josh (author)
Wed Aug 06 12:50:02 -0700 2008
commit  e5b1ab7cc39ff57f9789ffda75fb33f72187775d
tree    dd161b502ccfaca2e408779107d37386aa368103
parent  73056500f88d569fa497d846dfe6b501a9e03739
...
463
464
465
466
 
467
468
469
...
463
464
465
 
466
467
468
469
0
@@ -463,7 +463,7 @@ module ActionView
0
       end
0
 
0
       private
0
-        COMPUTED_PUBLIC_PATHS = ActiveSupport::Cache::MemoryStore.new.silence!.threadsafe!
0
+        COMPUTED_PUBLIC_PATHS = ActiveSupport::Cache::MemoryStore.new.silence!
0
 
0
         # Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
0
         # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
...
39
40
41
42
43
44
45
46
47
48
...
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
...
39
40
41
 
 
 
 
42
43
44
...
111
112
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
115
116
0
@@ -39,10 +39,6 @@ module ActiveSupport
0
     class Store
0
       cattr_accessor :logger
0
 
0
-      def threadsafe!
0
-        extend ThreadSafety
0
-      end
0
-
0
       def silence!
0
         @silence = true
0
         self
0
@@ -115,20 +111,6 @@ module ActiveSupport
0
           logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !@silence && !@logger_off
0
         end
0
     end
0
-
0
-    module ThreadSafety #:nodoc:
0
-      def self.extended(object) #:nodoc:
0
-        object.instance_variable_set(:@mutex, Mutex.new)
0
-      end
0
-
0
-      %w(read write delete delete_matched exist? increment decrement).each do |method|
0
-        module_eval <<-EOS, __FILE__, __LINE__
0
-          def #{method}(*args)
0
-            @mutex.synchronize { super }
0
-          end
0
-        EOS
0
-      end
0
-    end
0
   end
0
 end
0
 
...
3
4
5
 
 
 
 
 
 
 
6
7
8
9
10
 
 
 
 
11
12
13
14
15
 
 
 
 
16
17
18
19
20
 
 
 
 
21
22
23
24
25
 
 
 
 
26
27
28
29
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
32
33
34
 
 
 
35
36
37
38
39
 
...
3
4
5
6
7
8
9
10
11
12
13
14
15
 
 
16
17
18
19
20
21
22
 
 
23
24
25
26
27
28
29
 
 
30
31
32
33
34
35
36
 
 
37
38
39
40
41
42
43
 
 
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
 
63
64
65
66
67
68
 
69
70
0
@@ -3,36 +3,67 @@ module ActiveSupport
0
     class MemoryStore < Store
0
       def initialize
0
         @data = {}
0
+        @mutex = Mutex.new
0
+      end
0
+
0
+      def fetch(key, options = {})
0
+        @mutex.synchronize do
0
+          super
0
+        end
0
       end
0
 
0
       def read(name, options = nil)
0
-        super
0
-        @data[name]
0
+        @mutex.synchronize do
0
+          super
0
+          @data[name]
0
+        end
0
       end
0
 
0
       def write(name, value, options = nil)
0
-        super
0
-        @data[name] = value
0
+        @mutex.synchronize do
0
+          super
0
+          @data[name] = value
0
+        end
0
       end
0
 
0
       def delete(name, options = nil)
0
-        super
0
-        @data.delete(name)
0
+        @mutex.synchronize do
0
+          super
0
+          @data.delete(name)
0
+        end
0
       end
0
 
0
       def delete_matched(matcher, options = nil)
0
-        super
0
-        @data.delete_if { |k,v| k =~ matcher }
0
+        @mutex.synchronize do
0
+          super
0
+          @data.delete_if { |k,v| k =~ matcher }
0
+        end
0
       end
0
 
0
       def exist?(name,options = nil)
0
-        super
0
-        @data.has_key?(name)
0
+        @mutex.synchronize do
0
+          super
0
+          @data.has_key?(name)
0
+        end
0
+      end
0
+
0
+      def increment(key, amount = 1)
0
+        @mutex.synchronize do
0
+          super
0
+        end
0
+      end
0
+
0
+      def decrement(key, amount = 1)
0
+        @mutex.synchronize do
0
+          super
0
+        end
0
       end
0
 
0
       def clear
0
-        @data.clear
0
+        @mutex.synchronize do
0
+          @data.clear
0
+        end
0
       end
0
     end
0
   end
0
-end
0
\ No newline at end of file
0
+end
...
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
...
70
71
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
@@ -70,70 +70,3 @@ uses_mocha 'high-level cache store tests' do
0
     end
0
   end
0
 end
0
-
0
-class ThreadSafetyCacheStoreTest < Test::Unit::TestCase
0
-  def setup
0
-    @cache = ActiveSupport::Cache.lookup_store(:memory_store).threadsafe!
0
-    @cache.write('foo', 'bar')
0
-
0
-    # No way to have mocha proxy to the original method
0
-    @mutex = @cache.instance_variable_get(:@mutex)
0
-    @mutex.instance_eval %(
0
-      def calls; @calls; end
0
-      def synchronize
0
-        @calls ||= 0
0
-        @calls += 1
0
-        yield
0
-      end
0
-    )
0
-  end
0
-
0
-  def test_read_is_synchronized
0
-    assert_equal 'bar', @cache.read('foo')
0
-    assert_equal 1, @mutex.calls
0
-  end
0
-
0
-  def test_write_is_synchronized
0
-    @cache.write('foo', 'baz')
0
-    assert_equal 'baz', @cache.read('foo')
0
-    assert_equal 2, @mutex.calls
0
-  end
0
-
0
-  def test_delete_is_synchronized
0
-    assert_equal 'bar', @cache.read('foo')
0
-    @cache.delete('foo')
0
-    assert_equal nil, @cache.read('foo')
0
-    assert_equal 3, @mutex.calls
0
-  end
0
-
0
-  def test_delete_matched_is_synchronized
0
-    assert_equal 'bar', @cache.read('foo')
0
-    @cache.delete_matched(/foo/)
0
-    assert_equal nil, @cache.read('foo')
0
-    assert_equal 3, @mutex.calls
0
-  end
0
-
0
-  def test_fetch_is_synchronized
0
-    assert_equal 'bar', @cache.fetch('foo') { 'baz' }
0
-    assert_equal 'fu', @cache.fetch('bar') { 'fu' }
0
-    assert_equal 3, @mutex.calls
0
-  end
0
-
0
-  def test_exist_is_synchronized
0
-    assert @cache.exist?('foo')
0
-    assert !@cache.exist?('bar')
0
-    assert_equal 2, @mutex.calls
0
-  end
0
-
0
-  def test_increment_is_synchronized
0
-    @cache.write('foo_count', 1)
0
-    assert_equal 2, @cache.increment('foo_count')
0
-    assert_equal 4, @mutex.calls
0
-  end
0
-
0
-  def test_decrement_is_synchronized
0
-    @cache.write('foo_count', 1)
0
-    assert_equal 0, @cache.decrement('foo_count')
0
-    assert_equal 4, @mutex.calls
0
-  end
0
-end
...
776
777
778
779
780
781
782
...
776
777
778
 
779
780
781
0
@@ -776,7 +776,6 @@ Run `rake gems:install` to install the missing gems.
0
       self.dependency_loading = false
0
       self.active_record.allow_concurrency = true
0
       self.action_controller.allow_concurrency = true
0
-      self.to_prepare { Rails.cache.threadsafe! }
0
       self
0
     end
0
 

Comments

habermann24 Fri Aug 15 02:03:25 -0700 2008

I have no idea why, but this changeset breaks my app. Probably because I’m using i18n_yaml for translations with the new I18n API…it uses Rails caching mechanism…but now I don’t even get requests back from mongrel. They all magically seem to wait forever… Not sure if this is a bug caused by this changeset, or a misuse of the cache in that plugin, but since this commit, it breaks…