aboutsummaryrefslogtreecommitdiffstats
path: root/test/visitors/test_dispatch_contamination.rb
diff options
context:
space:
mode:
authorMatthew Draper <matthew@trebex.net>2017-06-05 06:25:46 +0930
committerMatthew Draper <matthew@trebex.net>2017-06-05 06:25:46 +0930
commitf509ad3c72ed55d9fb35848664a771815aa4599b (patch)
tree75603105025c11bd52be9ac71a6d18818a9a3053 /test/visitors/test_dispatch_contamination.rb
parentdba166bfd317bfeaba7211d2e65fbd760a184f6c (diff)
downloadrails-f509ad3c72ed55d9fb35848664a771815aa4599b.tar.gz
rails-f509ad3c72ed55d9fb35848664a771815aa4599b.tar.bz2
rails-f509ad3c72ed55d9fb35848664a771815aa4599b.zip
Test concurrency of visitor superclass fallback
Diffstat (limited to 'test/visitors/test_dispatch_contamination.rb')
-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