2018-03-04 15:09:32 +00:00
|
|
|
require_relative '../../spec_helper'
|
2017-05-07 12:04:49 +00:00
|
|
|
require 'thread'
|
|
|
|
|
|
|
|
describe "ConditionVariable#wait" do
|
2019-04-27 18:53:23 +02:00
|
|
|
it "calls #sleep on the given object" do
|
|
|
|
o = Object.new
|
|
|
|
o.should_receive(:sleep).with(1234)
|
|
|
|
|
|
|
|
cv = ConditionVariable.new
|
|
|
|
|
|
|
|
cv.wait(o, 1234)
|
|
|
|
end
|
|
|
|
|
2020-02-06 15:42:01 +09:00
|
|
|
it "can be woken up by ConditionVariable#signal" do
|
2017-05-07 12:04:49 +00:00
|
|
|
m = Mutex.new
|
|
|
|
cv = ConditionVariable.new
|
|
|
|
in_synchronize = false
|
|
|
|
|
|
|
|
th = Thread.new do
|
|
|
|
m.synchronize do
|
|
|
|
in_synchronize = true
|
2020-02-06 15:42:01 +09:00
|
|
|
cv.wait(m)
|
2017-05-07 12:04:49 +00:00
|
|
|
end
|
2020-02-06 15:42:01 +09:00
|
|
|
:success
|
2017-05-07 12:04:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# wait for m to acquire the mutex
|
|
|
|
Thread.pass until in_synchronize
|
|
|
|
# wait until th is sleeping (ie waiting)
|
2020-02-06 13:36:02 +09:00
|
|
|
Thread.pass until th.stop?
|
2017-05-07 12:04:49 +00:00
|
|
|
|
|
|
|
m.synchronize { cv.signal }
|
2020-02-06 15:42:01 +09:00
|
|
|
th.value.should == :success
|
2017-05-07 12:04:49 +00:00
|
|
|
end
|
2018-08-17 09:51:26 +00:00
|
|
|
|
2019-12-27 16:46:08 +01:00
|
|
|
it "can be interrupted by Thread#run" do
|
|
|
|
m = Mutex.new
|
|
|
|
cv = ConditionVariable.new
|
|
|
|
in_synchronize = false
|
|
|
|
|
|
|
|
th = Thread.new do
|
|
|
|
m.synchronize do
|
|
|
|
in_synchronize = true
|
|
|
|
cv.wait(m)
|
|
|
|
end
|
|
|
|
:success
|
|
|
|
end
|
|
|
|
|
|
|
|
# wait for m to acquire the mutex
|
|
|
|
Thread.pass until in_synchronize
|
|
|
|
# wait until th is sleeping (ie waiting)
|
2020-02-06 13:36:02 +09:00
|
|
|
Thread.pass until th.stop?
|
2019-12-27 16:46:08 +01:00
|
|
|
|
|
|
|
th.run
|
|
|
|
th.value.should == :success
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can be interrupted by Thread#wakeup" do
|
|
|
|
m = Mutex.new
|
|
|
|
cv = ConditionVariable.new
|
|
|
|
in_synchronize = false
|
|
|
|
|
|
|
|
th = Thread.new do
|
|
|
|
m.synchronize do
|
|
|
|
in_synchronize = true
|
|
|
|
cv.wait(m)
|
|
|
|
end
|
|
|
|
:success
|
|
|
|
end
|
|
|
|
|
|
|
|
# wait for m to acquire the mutex
|
|
|
|
Thread.pass until in_synchronize
|
|
|
|
# wait until th is sleeping (ie waiting)
|
2020-02-06 13:36:02 +09:00
|
|
|
Thread.pass until th.stop?
|
2019-12-27 16:46:08 +01:00
|
|
|
|
|
|
|
th.wakeup
|
|
|
|
th.value.should == :success
|
|
|
|
end
|
|
|
|
|
2018-08-18 13:52:53 +00:00
|
|
|
it "reacquires the lock even if the thread is killed" do
|
2018-08-17 09:51:26 +00:00
|
|
|
m = Mutex.new
|
|
|
|
cv = ConditionVariable.new
|
|
|
|
in_synchronize = false
|
2018-08-18 13:52:53 +00:00
|
|
|
owned = nil
|
2018-08-17 09:51:26 +00:00
|
|
|
|
|
|
|
th = Thread.new do
|
|
|
|
m.synchronize do
|
|
|
|
in_synchronize = true
|
2018-08-18 13:52:53 +00:00
|
|
|
begin
|
|
|
|
cv.wait(m)
|
|
|
|
ensure
|
|
|
|
owned = m.owned?
|
|
|
|
$stderr.puts "\nThe Thread doesn't own the Mutex!" unless owned
|
|
|
|
end
|
2018-08-17 09:51:26 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# wait for m to acquire the mutex
|
|
|
|
Thread.pass until in_synchronize
|
|
|
|
# wait until th is sleeping (ie waiting)
|
2020-02-06 13:36:02 +09:00
|
|
|
Thread.pass until th.stop?
|
2018-08-17 09:51:26 +00:00
|
|
|
|
|
|
|
th.kill
|
|
|
|
th.join
|
|
|
|
|
2018-08-18 13:52:53 +00:00
|
|
|
owned.should == true
|
2018-08-17 09:51:26 +00:00
|
|
|
end
|
|
|
|
|
2018-09-25 10:41:16 +00:00
|
|
|
ruby_bug '#14999', ''...'2.5' do
|
|
|
|
it "reacquires the lock even if the thread is killed after being signaled" do
|
|
|
|
m = Mutex.new
|
|
|
|
cv = ConditionVariable.new
|
|
|
|
in_synchronize = false
|
|
|
|
owned = nil
|
|
|
|
|
|
|
|
th = Thread.new do
|
|
|
|
m.synchronize do
|
|
|
|
in_synchronize = true
|
|
|
|
begin
|
|
|
|
cv.wait(m)
|
|
|
|
ensure
|
|
|
|
owned = m.owned?
|
|
|
|
$stderr.puts "\nThe Thread doesn't own the Mutex!" unless owned
|
|
|
|
end
|
2018-08-18 13:52:53 +00:00
|
|
|
end
|
2018-08-17 09:51:26 +00:00
|
|
|
end
|
|
|
|
|
2018-09-25 10:41:16 +00:00
|
|
|
# wait for m to acquire the mutex
|
|
|
|
Thread.pass until in_synchronize
|
|
|
|
# wait until th is sleeping (ie waiting)
|
2020-02-06 13:36:02 +09:00
|
|
|
Thread.pass until th.stop?
|
2018-09-25 10:41:16 +00:00
|
|
|
|
|
|
|
m.synchronize {
|
|
|
|
cv.signal
|
|
|
|
# Wait that the thread is blocked on acquiring the Mutex
|
|
|
|
sleep 0.001
|
|
|
|
# Kill the thread, yet the thread should first acquire the Mutex before going on
|
|
|
|
th.kill
|
|
|
|
}
|
2018-08-17 09:51:26 +00:00
|
|
|
|
2018-09-25 10:41:16 +00:00
|
|
|
th.join
|
|
|
|
owned.should == true
|
|
|
|
end
|
2018-08-17 09:51:26 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "supports multiple Threads waiting on the same ConditionVariable and Mutex" do
|
|
|
|
m = Mutex.new
|
|
|
|
cv = ConditionVariable.new
|
|
|
|
n_threads = 4
|
|
|
|
events = []
|
|
|
|
|
|
|
|
threads = n_threads.times.map {
|
|
|
|
Thread.new {
|
|
|
|
m.synchronize {
|
|
|
|
events << :t_in_synchronize
|
|
|
|
cv.wait(m)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Thread.pass until m.synchronize { events.size } == n_threads
|
2020-02-06 13:36:02 +09:00
|
|
|
Thread.pass until threads.any?(&:stop?)
|
2018-08-17 09:51:26 +00:00
|
|
|
m.synchronize do
|
|
|
|
threads.each { |t|
|
|
|
|
# Cause interactions with the waiting threads.
|
|
|
|
# On TruffleRuby, this causes a safepoint which has interesting
|
|
|
|
# interactions with the ConditionVariable.
|
|
|
|
bt = t.backtrace
|
|
|
|
bt.should be_kind_of(Array)
|
|
|
|
bt.size.should >= 2
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
cv.broadcast
|
|
|
|
threads.each(&:join)
|
|
|
|
end
|
2017-05-07 12:04:49 +00:00
|
|
|
end
|