summaryrefslogtreecommitdiff
path: root/test/thread/test_thread.rb
blob: f4e9170ca6522a77b68135fbcb73fc305a3f104b (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
require 'thread'
require 'test/unit'

class TC_Thread < Test::Unit::TestCase
  def setup
    Thread.abort_on_exception = true
  end
  def teardown
    Thread.abort_on_exception = false
  end
  def test_condvar
    mutex = Mutex.new
    condvar = ConditionVariable.new
    result = []
    mutex.synchronize do
      t = Thread.new do
        mutex.synchronize do
          result << 1
          condvar.signal
        end
      end

      result << 0
      condvar.wait(mutex)
      result << 2
      t.join
    end
    assert_equal([0, 1, 2], result)
  end

  def test_condvar_wait_not_owner
    mutex = Mutex.new
    condvar = ConditionVariable.new

    assert_raises(ThreadError) { condvar.wait(mutex) }
  end

  def test_condvar_wait_exception_handling
    # Calling wait in the only thread running should raise a ThreadError of
    # 'stopping only thread'
    mutex = Mutex.new
    condvar = ConditionVariable.new

    Thread.abort_on_exception = false

    locked = false
    thread = Thread.new do
      mutex.synchronize do
        begin
          condvar.wait(mutex)
        rescue Exception
          locked = mutex.locked?
          raise
        end
      end
    end

    while !thread.stop?
      sleep(0.1)
    end

    thread.raise Interrupt, "interrupt a dead condition variable"
    assert_raises(Interrupt) { thread.value }
    assert(locked)
  end

  def test_local_barrier
    dir = File.dirname(__FILE__)
    lbtest = File.join(dir, "lbtest.rb")
    $:.unshift File.join(File.dirname(dir), 'ruby')
    require 'envutil'
    $:.shift
    10.times {
      result = `#{EnvUtil.rubybin} #{lbtest}`
      assert(!$?.coredump?, '[ruby-dev:30653]')
      assert_equal("exit.", result[/.*\Z/], '[ruby-dev:30653]')
    }
  end

  def test_queue_rescue
    require "timeout"
    queue = Queue.new
    assert_raises(Timeout::Error) {Timeout.timeout(0.001) {queue.pop}}
    queue.push(1)
    assert_nothing_raised("[ruby-dev:37545]") {assert_equal(1, queue.pop)}
    assert(queue.empty?)
  end

  # This test checks that a thread in Mutex#lock which is raised is
  # completely removed from the wait_list of the mutex
  def test_mutex_exception_handling
    m = Mutex.new
    m.lock

    sleeping = false
    t = Thread.new do
      begin
        m.lock
      rescue
      end

      sleeping = true
      # Keep that thread alive: if the thread returns, the test method
      # won't be able to check that +m+ has not been taken (dead mutex
      # owners are ignored)
      sleep
    end

    # Wait for t to wait for the mutex and raise it
    while true
      sleep 0.1
      break if t.stop?
    end
    t.raise ArgumentError
    assert(t.alive? || sleeping)

    # Wait for +t+ to reach the sleep
    while true
      sleep 0.1
      break if t.stop?
    end

    # Now unlock. The mutex should be free, so Mutex#unlock should return nil
    assert(! m.unlock)
  end

  def test_mutex_join
    m = Mutex.new
    m.lock
    wt2 = Thread.new do
      m.lock
      sleep 0.5
      m.unlock
    end

    # Ensure wt2 is waiting on m
    sleep 0.1

    wt1 = Thread.new do
      m.lock
      m.unlock
    end
    # Ensure wt1 is waiting on m
    sleep 0.1

    # Give it to wt2
    m.unlock

    assert_nothing_raised {wt1.join}
  end
end