summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-10-16 01:10:44 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-10-16 01:10:44 +0000
commitac0b7f38fe41d9e90faa8651dcee32d2c9fe1690 (patch)
treef36d997a553c3e73724057eaef4b3af23e045783
parentd2c1ab806d521cb024089f5558dc82e134db68aa (diff)
* test/ruby/test_{queue,thread}.rb: backported from trunk.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@25361 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--test/ruby/lbtest.rb48
-rw-r--r--test/ruby/test_queue.rb38
-rw-r--r--test/ruby/test_thread.rb442
3 files changed, 528 insertions, 0 deletions
diff --git a/test/ruby/lbtest.rb b/test/ruby/lbtest.rb
new file mode 100644
index 0000000000..df7872dc76
--- /dev/null
+++ b/test/ruby/lbtest.rb
@@ -0,0 +1,48 @@
+require 'thread'
+
+class LocalBarrier
+ def initialize(n)
+ @wait = Queue.new
+ @done = Queue.new
+ @keeper = begin_keeper(n)
+ end
+
+ def sync
+ @done.push(true)
+ @wait.pop
+ end
+
+ def join
+ @keeper.join
+ end
+
+ private
+ def begin_keeper(n)
+ Thread.start do
+ n.times do
+ @done.pop
+ end
+ n.times do
+ @wait.push(true)
+ end
+ end
+ end
+end
+
+n = 10
+
+lb = LocalBarrier.new(n)
+
+(n - 1).times do |i|
+ Thread.start do
+ sleep((rand(n) + 1) / 10.0)
+ puts "#{i}: done"
+ lb.sync
+ puts "#{i}: cont"
+ end
+end
+
+lb.sync
+puts "#{n-1}: cone"
+
+puts "exit."
diff --git a/test/ruby/test_queue.rb b/test/ruby/test_queue.rb
new file mode 100644
index 0000000000..16e7548d1a
--- /dev/null
+++ b/test/ruby/test_queue.rb
@@ -0,0 +1,38 @@
+require 'test/unit'
+require 'thread'
+
+class TestQueue < Test::Unit::TestCase
+ def test_queue
+ grind(5, 10_000, 15, Queue)
+ end
+
+ def test_sized_queue
+ grind(5, 10_000, 15, SizedQueue, 10_000)
+ end
+
+ def grind(num_threads, num_objects, num_iterations, klass, *args)
+ from_workers = klass.new(*args)
+ to_workers = klass.new(*args)
+
+ workers = (1..num_threads).map {
+ Thread.new {
+ while object = to_workers.pop
+ from_workers.push object
+ end
+ }
+ }
+
+ Thread.new {
+ num_iterations.times {
+ num_objects.times { to_workers.push 99 }
+ num_objects.times { from_workers.pop }
+ }
+ }.join
+
+ num_threads.times { to_workers.push nil }
+ workers.each { |t| t.join }
+
+ assert 0, from_workers.size
+ assert 0, to_workers.size
+ end
+end
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
new file mode 100644
index 0000000000..f9cf32efd1
--- /dev/null
+++ b/test/ruby/test_thread.rb
@@ -0,0 +1,442 @@
+require 'test/unit'
+require 'thread'
+require File.join(File.dirname(__FILE__), 'envutil')
+
+class TestThread < Test::Unit::TestCase
+ class Thread < ::Thread
+ Threads = []
+ def self.new(*)
+ th = super
+ th.abort_on_exception = true
+ Threads << th
+ th
+ end
+ end
+
+ def setup
+ Thread::Threads.clear
+ end
+
+ def teardown
+ Thread::Threads.each do |t|
+ t.kill if t.alive?
+ begin
+ t.join
+ rescue Exception
+ end
+ end
+ end
+
+ def test_mutex_synchronize
+ m = Mutex.new
+ r = 0
+ max = 100
+ (1..max).map{
+ Thread.new{
+ i=0
+ while i<max*max
+ i+=1
+ m.synchronize{
+ r += 1
+ }
+ end
+ }
+ }.each{|e|
+ e.join
+ }
+ assert_equal(max * max * max, r)
+ 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_raise(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
+
+ locked = false
+ thread = Thread.new do
+ Thread.current.abort_on_exception = false
+ mutex.synchronize do
+ begin
+ condvar.wait(mutex)
+ rescue Exception
+ locked = mutex.locked?
+ raise
+ end
+ end
+ end
+
+ until thread.stop?
+ sleep(0.1)
+ end
+
+ assert_raise(SystemExit) {
+ thread.raise SystemExit, "interrupt a dead condition variable"
+ }
+ 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_priority
+ c1 = c2 = 0
+ t1 = Thread.new { loop { c1 += 1 } }
+ t1.priority = -1
+ t2 = Thread.new { loop { c2 += 1 } }
+ t2.priority = -3
+ assert_equal(-1, t1.priority)
+ assert_equal(-3, t2.priority)
+ sleep 0.5
+ t1.kill
+ t2.kill
+ assert(c1 > c2, "[ruby-dev:33124]")
+ end
+
+ def test_new
+ assert_raise(ThreadError) do
+ Thread.new
+ end
+
+ t1 = Thread.new { sleep }
+ assert_raise(ThreadError) do
+ t1.instance_eval { initialize { } }
+ end
+
+ t2 = Thread.new(&method(:sleep).to_proc)
+ assert_raise(ThreadError) do
+ t2.instance_eval { initialize { } }
+ end
+
+ ensure
+ t1.kill if t1
+ t2.kill if t2
+ end
+
+ def test_join
+ t = Thread.new { sleep }
+ assert_nil(t.join(0.5))
+
+ ensure
+ t.kill if t
+ end
+
+ def test_join2
+ t1 = Thread.new { sleep(1.5) }
+ t2 = Thread.new do
+ t1.join(1)
+ end
+ t3 = Thread.new do
+ sleep 0.5
+ t1.join
+ end
+ assert_nil(t2.value)
+ assert_equal(t1, t3.value)
+
+ ensure
+ t1.kill if t1
+ t2.kill if t2
+ t3.kill if t3
+ end
+
+ def test_exit
+ s = 0
+ Thread.new do
+ s += 1
+ Thread.exit
+ s += 2
+ end.join
+ assert_equal(1, s)
+ end
+
+ def test_wakeup
+ s = 0
+ t = Thread.new do
+ s += 1
+ Thread.stop
+ s += 1
+ end
+ sleep 0.5
+ assert_equal(1, s)
+ t.wakeup
+ sleep 0.5
+ assert_equal(2, s)
+ assert_raise(ThreadError) { t.wakeup }
+
+ ensure
+ t.kill if t
+ end
+
+ def test_status_and_stop_p
+ a = ::Thread.new { raise("die now") }
+ b = Thread.new { Thread.stop }
+ c = Thread.new { Thread.exit }
+ d = Thread.new { sleep }
+ e = Thread.current
+ sleep 0.5
+
+ assert_equal(nil, a.status)
+ assert(a.stop?)
+
+ assert_equal("sleep", b.status)
+ assert(b.stop?)
+
+ assert_equal(false, c.status)
+ assert_match(/^#<TestThread::Thread:.* dead>$/, c.inspect)
+ assert(c.stop?)
+
+ d.kill
+ assert_equal(false, d.status)
+ assert(d.stop?)
+
+ assert_equal("run", e.status)
+ assert(!e.stop?)
+
+ ensure
+ a.kill if a
+ b.kill if b
+ c.kill if c
+ d.kill if d
+ end
+
+ def test_safe_level
+ t = Thread.new { $SAFE = 3; sleep }
+ sleep 0.5
+ assert_equal(0, Thread.current.safe_level)
+ assert_equal(3, t.safe_level)
+
+ ensure
+ t.kill if t
+ end
+
+ def test_thread_local
+ t = Thread.new { sleep }
+
+ assert_equal(false, t.key?(:foo))
+
+ t["foo"] = "foo"
+ t["bar"] = "bar"
+ t["baz"] = "baz"
+
+ assert_equal(true, t.key?(:foo))
+ assert_equal(true, t.key?("foo"))
+ assert_equal(false, t.key?(:qux))
+ assert_equal(false, t.key?("qux"))
+
+ assert_equal([:bar, :baz, :foo], t.keys.sort_by{|n|n.to_s})
+
+ ensure
+ t.kill if t
+ end
+
+ def test_thread_local_security
+ t = Thread.new { sleep }
+
+ assert_raise(SecurityError) do
+ Thread.new { $SAFE = 4; t[:foo] }.join
+ end
+
+ assert_raise(SecurityError) do
+ Thread.new { $SAFE = 4; t[:foo] = :baz }.join
+ end
+
+ assert_raise(TypeError) do
+ Thread.new do
+ Thread.current[:foo] = :bar
+ Thread.current.freeze
+ Thread.current[:foo] = :baz
+ end.join
+ end
+ end
+
+ def test_select_wait
+ assert_nil(IO.select(nil, nil, nil, 1))
+ t = Thread.new do
+ IO.select(nil, nil, nil, nil)
+ end
+ sleep 0.5
+ t.kill
+ end
+
+ def test_mutex_deadlock
+ m = Mutex.new
+ m.synchronize do
+ assert_raise(ThreadError) do
+ m.synchronize do
+ assert(false)
+ end
+ end
+ end
+ end
+
+ def test_mutex_interrupt
+ m = Mutex.new
+ m.lock
+ t = Thread.new do
+ m.lock
+ :foo
+ end
+ sleep 0.5
+ t.kill
+ assert_nil(t.value)
+ end
+
+ def test_mutex_illegal_unlock
+ m = Mutex.new
+ m.lock
+ assert_raise(ThreadError) do
+ Thread.new do
+ m.unlock
+ end.join
+ end
+ end
+
+ def test_mutex_fifo_like_lock
+ m1 = Mutex.new
+ m2 = Mutex.new
+ m1.lock
+ m2.lock
+ m1.unlock
+ m2.unlock
+ assert_equal(false, m1.locked?)
+ assert_equal(false, m2.locked?)
+
+ m3 = Mutex.new
+ m1.lock
+ m2.lock
+ m3.lock
+ m1.unlock
+ m2.unlock
+ m3.unlock
+ assert_equal(false, m1.locked?)
+ assert_equal(false, m2.locked?)
+ assert_equal(false, m3.locked?)
+ end
+
+ def test_mutex_trylock
+ m = Mutex.new
+ assert_equal(true, m.try_lock)
+ assert_equal(false, m.try_lock, '[ruby-core:20943]')
+
+ Thread.new{
+ assert_equal(false, m.try_lock)
+ }.join
+
+ m.unlock
+ end
+
+ def test_recursive_error
+ o = Object.new
+ def o.inspect
+ Thread.current[:__recursive_key__][:inspect] = nil
+ super
+ end
+ assert_raise(TypeError) { [o].inspect }
+ end
+end
+
+class TestThreadGroup < Test::Unit::TestCase
+ def test_thread_init
+ thgrp = ThreadGroup.new
+ Thread.new{
+ thgrp.add(Thread.current)
+ assert_equal(thgrp, Thread.new{sleep 1}.group)
+ }.join
+ end
+
+ def test_frozen_thgroup
+ thgrp = ThreadGroup.new
+
+ t = Thread.new{1}
+ Thread.new{
+ thgrp.add(Thread.current)
+ thgrp.freeze
+ assert_raise(ThreadError) do
+ Thread.new{1}.join
+ end
+ assert_raise(ThreadError) do
+ thgrp.add(t)
+ end
+ assert_raise(ThreadError) do
+ ThreadGroup.new.add Thread.current
+ end
+ }.join
+ t.join
+ end
+
+ def test_enclosed_thgroup
+ thgrp = ThreadGroup.new
+ assert_equal(false, thgrp.enclosed?)
+
+ t = Thread.new{1}
+ Thread.new{
+ thgrp.add(Thread.current)
+ thgrp.enclose
+ assert_equal(true, thgrp.enclosed?)
+ assert_nothing_raised do
+ Thread.new{1}.join
+ end
+ assert_raise(ThreadError) do
+ thgrp.add t
+ end
+ assert_raise(ThreadError) do
+ ThreadGroup.new.add Thread.current
+ end
+ }.join
+ t.join
+ end
+
+ def test_uninitialized
+ c = Class.new(Thread)
+ c.class_eval { def initialize; end }
+ assert_raise(ThreadError) { c.new.start }
+ end
+
+ def test_backtrace
+ Thread.new{
+ assert_equal(Array, Thread.main.backtrace.class)
+ }.join
+
+ t = Thread.new{}
+ t.join
+ assert_equal(nil, t.backtrace)
+ end
+end