# -*- 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

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

