summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authornormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-03-27 09:28:37 +0000
committernormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-03-27 09:28:37 +0000
commita2d63ea2fb84c962abddae877e9493fc57cfce1a (patch)
tree4503e0aa911338f50abdcc580a169d56f9f14a9a /test
parent98e9444b5f33873fa3e8e8cdd4143771b1bc477e (diff)
thread_sync.c: avoid reaching across stacks of dead threads
rb_ensure is insufficient cleanup for fork and we must reinitialize all waitqueues in the child process. Unfortunately this increases the footprint of ConditionVariable, Queue and SizedQueue by 8 bytes on 32-bit (16 bytes on 64-bit). [ruby-core:86316] [Bug #14634] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62934 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'test')
-rw-r--r--test/thread/test_cv.rb19
-rw-r--r--test/thread/test_queue.rb48
2 files changed, 67 insertions, 0 deletions
diff --git a/test/thread/test_cv.rb b/test/thread/test_cv.rb
index 70cf4483a38..a093f373b18 100644
--- a/test/thread/test_cv.rb
+++ b/test/thread/test_cv.rb
@@ -219,4 +219,23 @@ INPUT
Marshal.dump(condvar)
end
end
+
+ def test_condvar_fork
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ thrs = (1..10).map do
+ Thread.new { mutex.synchronize { condvar.wait(mutex) } }
+ end
+ thrs.each { 3.times { Thread.pass } }
+ pid = fork do
+ mutex.synchronize { condvar.broadcast }
+ exit!(0)
+ end
+ _, s = Process.waitpid2(pid)
+ assert_predicate s, :success?, 'no segfault [ruby-core:86316] [Bug #14634]'
+ until thrs.empty?
+ mutex.synchronize { condvar.broadcast }
+ thrs.delete_if { |t| t.join(0.01) }
+ end
+ end if Process.respond_to?(:fork)
end
diff --git a/test/thread/test_queue.rb b/test/thread/test_queue.rb
index 4e6c9fa4c9f..d69ecf92b2c 100644
--- a/test/thread/test_queue.rb
+++ b/test/thread/test_queue.rb
@@ -565,4 +565,52 @@ class TestQueue < Test::Unit::TestCase
puts 'exit'
INPUT
end
+
+ def test_fork_while_queue_waiting
+ q = Queue.new
+ sq = SizedQueue.new(1)
+ thq = Thread.new { q.pop }
+ thsq = Thread.new { sq.pop }
+ Thread.pass until thq.stop? && thsq.stop?
+
+ pid = fork do
+ exit!(1) if q.num_waiting != 0
+ exit!(2) if sq.num_waiting != 0
+ exit!(6) unless q.empty?
+ exit!(7) unless sq.empty?
+ q.push :child_q
+ sq.push :child_sq
+ exit!(3) if q.pop != :child_q
+ exit!(4) if sq.pop != :child_sq
+ exit!(0)
+ end
+ _, s = Process.waitpid2(pid)
+ assert_predicate s, :success?, 'no segfault [ruby-core:86316] [Bug #14634]'
+
+ q.push :thq
+ sq.push :thsq
+ assert_equal :thq, thq.value
+ assert_equal :thsq, thsq.value
+
+ sq.push(1)
+ th = Thread.new { q.pop; sq.pop }
+ thsq = Thread.new { sq.push(2) }
+ Thread.pass until th.stop? && thsq.stop?
+ pid = fork do
+ exit!(1) if q.num_waiting != 0
+ exit!(2) if sq.num_waiting != 0
+ exit!(3) unless q.empty?
+ exit!(4) if sq.empty?
+ exit!(5) if sq.pop != 1
+ exit!(0)
+ end
+ _, s = Process.waitpid2(pid)
+ assert_predicate s, :success?, 'no segfault [ruby-core:86316] [Bug #14634]'
+
+ assert_predicate thsq, :stop?
+ assert_equal 1, sq.pop
+ assert_same sq, thsq.value
+ q.push('restart th')
+ assert_equal 2, th.value
+ end if Process.respond_to?(:fork)
end