mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
6987c8997e
* Previously this could lead to an invalid waiter entry and then trying to wake up that waiter would result in various issues in rb_mutex_unlock_th().
223 lines
3.8 KiB
Ruby
223 lines
3.8 KiB
Ruby
# frozen_string_literal: true
|
|
require 'test/unit'
|
|
require_relative 'scheduler'
|
|
|
|
class TestFiberMutex < Test::Unit::TestCase
|
|
def test_mutex_synchronize
|
|
mutex = Mutex.new
|
|
|
|
thread = Thread.new do
|
|
scheduler = Scheduler.new
|
|
Thread.current.scheduler = scheduler
|
|
|
|
Fiber.schedule do
|
|
assert_equal Thread.scheduler, scheduler
|
|
|
|
mutex.synchronize do
|
|
assert Thread.scheduler
|
|
end
|
|
end
|
|
end
|
|
|
|
thread.join
|
|
end
|
|
|
|
def test_mutex_interleaved_locking
|
|
mutex = Mutex.new
|
|
|
|
thread = Thread.new do
|
|
scheduler = Scheduler.new
|
|
Thread.current.scheduler = scheduler
|
|
|
|
Fiber.schedule do
|
|
mutex.lock
|
|
sleep 0.1
|
|
mutex.unlock
|
|
end
|
|
|
|
Fiber.schedule do
|
|
mutex.lock
|
|
sleep 0.1
|
|
mutex.unlock
|
|
end
|
|
|
|
scheduler.run
|
|
end
|
|
|
|
thread.join
|
|
end
|
|
|
|
def test_mutex_thread
|
|
mutex = Mutex.new
|
|
mutex.lock
|
|
|
|
thread = Thread.new do
|
|
scheduler = Scheduler.new
|
|
Thread.current.scheduler = scheduler
|
|
|
|
Fiber.schedule do
|
|
mutex.lock
|
|
sleep 0.1
|
|
mutex.unlock
|
|
end
|
|
|
|
scheduler.run
|
|
end
|
|
|
|
sleep 0.1
|
|
mutex.unlock
|
|
|
|
thread.join
|
|
end
|
|
|
|
def test_mutex_fiber_raise
|
|
mutex = Mutex.new
|
|
ran = false
|
|
|
|
main = Thread.new do
|
|
mutex.lock
|
|
|
|
thread = Thread.new do
|
|
scheduler = Scheduler.new
|
|
Thread.current.scheduler = scheduler
|
|
|
|
f = Fiber.schedule do
|
|
assert_raise_with_message(RuntimeError, "bye") do
|
|
assert_same scheduler, Thread.scheduler
|
|
mutex.lock
|
|
end
|
|
ran = true
|
|
end
|
|
|
|
Fiber.schedule do
|
|
f.raise "bye"
|
|
end
|
|
end
|
|
|
|
thread.join
|
|
end
|
|
|
|
main.join # causes mutex to be released
|
|
assert_equal false, mutex.locked?
|
|
assert_equal true, ran
|
|
end
|
|
|
|
def test_condition_variable
|
|
mutex = Mutex.new
|
|
condition = ConditionVariable.new
|
|
|
|
signalled = 0
|
|
|
|
thread = Thread.new do
|
|
scheduler = Scheduler.new
|
|
Thread.current.scheduler = scheduler
|
|
|
|
Fiber.schedule do
|
|
mutex.synchronize do
|
|
3.times do
|
|
condition.wait(mutex)
|
|
signalled += 1
|
|
end
|
|
end
|
|
end
|
|
|
|
Fiber.schedule do
|
|
3.times do
|
|
mutex.synchronize do
|
|
condition.signal
|
|
end
|
|
|
|
sleep 0.1
|
|
end
|
|
end
|
|
|
|
scheduler.run
|
|
end
|
|
|
|
thread.join
|
|
|
|
assert signalled > 1
|
|
end
|
|
|
|
def test_queue
|
|
queue = Queue.new
|
|
processed = 0
|
|
|
|
thread = Thread.new do
|
|
scheduler = Scheduler.new
|
|
Thread.current.scheduler = scheduler
|
|
|
|
Fiber.schedule do
|
|
3.times do |i|
|
|
queue << i
|
|
sleep 0.1
|
|
end
|
|
|
|
queue.close
|
|
end
|
|
|
|
Fiber.schedule do
|
|
while item = queue.pop
|
|
processed += 1
|
|
end
|
|
end
|
|
|
|
scheduler.run
|
|
end
|
|
|
|
thread.join
|
|
|
|
assert processed == 3
|
|
end
|
|
|
|
def test_queue_pop_waits
|
|
queue = Queue.new
|
|
running = false
|
|
|
|
thread = Thread.new do
|
|
scheduler = Scheduler.new
|
|
Thread.current.scheduler = scheduler
|
|
|
|
result = nil
|
|
Fiber.schedule do
|
|
result = queue.pop
|
|
end
|
|
|
|
running = true
|
|
scheduler.run
|
|
result
|
|
end
|
|
|
|
Thread.pass until running
|
|
sleep 0.1
|
|
|
|
queue << :done
|
|
assert_equal :done, thread.value
|
|
end
|
|
|
|
def test_mutex_deadlock
|
|
err = /No live threads left. Deadlock\?/
|
|
assert_in_out_err %W[-I#{__dir__} -], <<-RUBY, ['in synchronize'], err, success: false
|
|
require 'scheduler'
|
|
mutex = Mutex.new
|
|
|
|
thread = Thread.new do
|
|
scheduler = Scheduler.new
|
|
Thread.current.scheduler = scheduler
|
|
|
|
Fiber.schedule do
|
|
raise unless Thread.scheduler == scheduler
|
|
|
|
mutex.synchronize do
|
|
puts 'in synchronize'
|
|
Fiber.yield
|
|
end
|
|
end
|
|
|
|
mutex.lock
|
|
end
|
|
|
|
thread.join
|
|
RUBY
|
|
end
|
|
end
|