aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorMatthew Draper <matthew@trebex.net>2017-06-05 06:32:42 +0930
committerGitHub <noreply@github.com>2017-06-05 06:32:42 +0930
commitde0a83e11cae11ba0674bd197296326e53354597 (patch)
treef2fc8b94904a06e13e4442287541ed48100a64c9 /test
parent03a2de4d063f52250105c08c0c05d64c5d42561d (diff)
parentf509ad3c72ed55d9fb35848664a771815aa4599b (diff)
downloadrails-de0a83e11cae11ba0674bd197296326e53354597.tar.gz
rails-de0a83e11cae11ba0674bd197296326e53354597.tar.bz2
rails-de0a83e11cae11ba0674bd197296326e53354597.zip
Merge pull request #472 from film42/master
Make Visitor visit thread safe by holding dispatch method reference
Diffstat (limited to 'test')
-rw-r--r--test/visitors/test_dispatch_contamination.rb49
1 files changed, 49 insertions, 0 deletions
diff --git a/test/visitors/test_dispatch_contamination.rb b/test/visitors/test_dispatch_contamination.rb
index 6422a6dff3..71792594d6 100644
--- a/test/visitors/test_dispatch_contamination.rb
+++ b/test/visitors/test_dispatch_contamination.rb
@@ -1,8 +1,42 @@
# frozen_string_literal: true
require 'helper'
+require 'concurrent'
module Arel
module Visitors
+ class DummyVisitor < Visitor
+ def initialize
+ super
+ @barrier = Concurrent::CyclicBarrier.new(2)
+ end
+
+ def visit_Arel_Visitors_DummySuperNode node
+ 42
+ end
+
+ # This is terrible, but it's the only way to reliably reproduce
+ # the possible race where two threads attempt to correct the
+ # dispatch hash at the same time.
+ def send *args
+ super
+ rescue
+ # Both threads try (and fail) to dispatch to the subclass's name
+ @barrier.wait
+ raise
+ ensure
+ # Then one thread successfully completes (updating the dispatch
+ # table in the process) before the other finishes raising its
+ # exception.
+ Thread.current[:delay].wait if Thread.current[:delay]
+ end
+ end
+
+ class DummySuperNode
+ end
+
+ class DummySubNode < DummySuperNode
+ end
+
describe 'avoiding contamination between visitor dispatch tables' do
before do
@connection = Table.engine.connection
@@ -17,6 +51,21 @@ module Arel
assert_equal "( TRUE UNION FALSE )", node.to_sql
end
+
+ it 'is threadsafe when implementing superclass fallback' do
+ visitor = DummyVisitor.new
+ main_thread_finished = Concurrent::Event.new
+
+ racing_thread = Thread.new do
+ Thread.current[:delay] = main_thread_finished
+ visitor.accept DummySubNode.new
+ end
+
+ assert_equal 42, visitor.accept(DummySubNode.new)
+ main_thread_finished.set
+
+ assert_equal 42, racing_thread.value
+ end
end
end
end