summaryrefslogtreecommitdiff
path: root/spec/ruby/library/conditionvariable/wait_spec.rb
blob: 9a68c2b5a14ec04901ac5263dfd63c622f3ed014 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
require_relative '../../spec_helper'
require 'thread'

describe "ConditionVariable#wait" do
  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

  it "can be woken up by ConditionVariable#signal" 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)
    Thread.pass until th.stop?

    m.synchronize { cv.signal }
    th.value.should == :success
  end

  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)
    Thread.pass until th.stop?

    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)
    Thread.pass until th.stop?

    th.wakeup
    th.value.should == :success
  end

  it "reacquires the lock even if the thread is killed" 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
      end
    end

    # wait for m to acquire the mutex
    Thread.pass until in_synchronize
    # wait until th is sleeping (ie waiting)
    Thread.pass until th.stop?

    th.kill
    th.join

    owned.should == true
  end

  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
      end
    end

    # wait for m to acquire the mutex
    Thread.pass until in_synchronize
    # wait until th is sleeping (ie waiting)
    Thread.pass until th.stop?

    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
    }

    th.join
    owned.should == true
  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
    Thread.pass until threads.any?(&:stop?)
    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
end