aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/relation
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2012-11-10 21:41:31 +0000
committerJon Leighton <j@jonathanleighton.com>2012-11-10 21:41:31 +0000
commit12d6d3a6f8914d272529d79cba461759e2dedf09 (patch)
tree881f1c08c8bfc22f94e2d18cef79311d97b0a232 /activerecord/lib/active_record/relation
parent8ec51669d46b47027c848001d228d98ee0611f8b (diff)
downloadrails-12d6d3a6f8914d272529d79cba461759e2dedf09.tar.gz
rails-12d6d3a6f8914d272529d79cba461759e2dedf09.tar.bz2
rails-12d6d3a6f8914d272529d79cba461759e2dedf09.zip
Make ActiveRecord::Delegation#method_missing threadsafe
Two threads may be in method_missing at the same time. If so, they might both try to define the same delegator method. Such a situation probably wouldn't result in a particularly spectacular bug as one method would probably just be overridden by an identical method, but it could cause warnings to pop up. (It could be worse if method definition is non-atomic in a particular implementation.) (We will also need this mutex shortly anyway, see #8127.)
Diffstat (limited to 'activerecord/lib/active_record/relation')
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb24
1 files changed, 21 insertions, 3 deletions
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index ab8b36c8ab..dbfa92bbbd 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -1,3 +1,4 @@
+require 'thread'
module ActiveRecord
module Delegation # :nodoc:
@@ -6,6 +7,8 @@ module ActiveRecord
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
:connection, :columns_hash, :auto_explain_threshold_in_seconds, :to => :klass
+ @@delegation_mutex = Mutex.new
+
def self.delegate_to_scoped_klass(method)
if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
module_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -32,13 +35,28 @@ module ActiveRecord
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
- ::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
+ @@delegation_mutex.synchronize do
+ unless ::ActiveRecord::Delegation.method_defined?(method)
+ ::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
+ end
+ end
+
scoping { @klass.send(method, *args, &block) }
elsif Array.method_defined?(method)
- ::ActiveRecord::Delegation.delegate method, :to => :to_a
+ @@delegation_mutex.synchronize do
+ unless ::ActiveRecord::Delegation.method_defined?(method)
+ ::ActiveRecord::Delegation.delegate method, :to => :to_a
+ end
+ end
+
to_a.send(method, *args, &block)
elsif arel.respond_to?(method)
- ::ActiveRecord::Delegation.delegate method, :to => :arel
+ @@delegation_mutex.synchronize do
+ unless ::ActiveRecord::Delegation.method_defined?(method)
+ ::ActiveRecord::Delegation.delegate method, :to => :arel
+ end
+ end
+
arel.send(method, *args, &block)
else
super