summaryrefslogtreecommitdiff
path: root/test/thread/test_thread.rb
blob: e3246d149c58cfc2bde40b9fbf29afc39ac990df (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
# -*- ruby-indent-level: 4 -*-
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