diff options
author | Jon Leighton <j@jonathanleighton.com> | 2012-11-10 21:41:31 +0000 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2012-11-10 21:41:31 +0000 |
commit | 12d6d3a6f8914d272529d79cba461759e2dedf09 (patch) | |
tree | 881f1c08c8bfc22f94e2d18cef79311d97b0a232 /activerecord/lib/active_record/relation | |
parent | 8ec51669d46b47027c848001d228d98ee0611f8b (diff) | |
download | rails-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.rb | 24 |
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 |