1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/spec/ruby/core/thread/fixtures/classes.rb
2019-12-11 16:52:06 +09:00

297 lines
6.8 KiB
Ruby

module ThreadSpecs
class SubThread < Thread
def initialize(*args)
super { args.first << 1 }
end
end
class Status
attr_reader :thread, :inspect, :status, :to_s
def initialize(thread)
@thread = thread
@alive = thread.alive?
@inspect = thread.inspect
@to_s = thread.to_s
@status = thread.status
@stop = thread.stop?
end
def alive?
@alive
end
def stop?
@stop
end
end
# TODO: In the great Thread spec rewrite, abstract this
class << self
attr_accessor :state
end
def self.clear_state
@state = nil
end
def self.spin_until_sleeping(t)
Thread.pass while t.status and t.status != "sleep"
end
def self.sleeping_thread
Thread.new do
begin
sleep
ScratchPad.record :woken
rescue Object => e
ScratchPad.record e
end
end
end
def self.running_thread
Thread.new do
begin
ThreadSpecs.state = :running
loop { Thread.pass }
ScratchPad.record :woken
rescue Object => e
ScratchPad.record e
end
end
end
def self.completed_thread
Thread.new {}
end
def self.status_of_current_thread
Thread.new { Status.new(Thread.current) }.value
end
def self.status_of_running_thread
t = running_thread
Thread.pass while t.status and t.status != "run"
status = Status.new t
t.kill
t.join
status
end
def self.status_of_completed_thread
t = completed_thread
t.join
Status.new t
end
def self.status_of_sleeping_thread
t = sleeping_thread
Thread.pass while t.status and t.status != 'sleep'
status = Status.new t
t.run
t.join
status
end
def self.status_of_blocked_thread
m = Mutex.new
m.lock
t = Thread.new { m.lock }
Thread.pass while t.status and t.status != 'sleep'
status = Status.new t
m.unlock
t.join
status
end
def self.status_of_killed_thread
t = Thread.new { sleep }
Thread.pass while t.status and t.status != 'sleep'
t.kill
t.join
Status.new t
end
def self.status_of_thread_with_uncaught_exception
t = Thread.new {
Thread.current.report_on_exception = false
raise "error"
}
begin
t.join
rescue RuntimeError
end
Status.new t
end
def self.status_of_dying_running_thread
status = nil
t = dying_thread_ensures { status = Status.new Thread.current }
t.join
status
end
def self.status_of_dying_sleeping_thread
t = dying_thread_ensures { Thread.stop; }
Thread.pass while t.status and t.status != 'sleep'
status = Status.new t
t.wakeup
t.join
status
end
def self.status_of_dying_thread_after_sleep
status = nil
t = dying_thread_ensures {
Thread.stop
status = Status.new(Thread.current)
}
Thread.pass while t.status and t.status != 'sleep'
t.wakeup
Thread.pass while t.status and t.status == 'sleep'
t.join
status
end
def self.dying_thread_ensures(kill_method_name=:kill)
Thread.new do
Thread.current.report_on_exception = false
begin
Thread.current.send(kill_method_name)
ensure
yield
end
end
end
def self.dying_thread_with_outer_ensure(kill_method_name=:kill)
Thread.new do
Thread.current.report_on_exception = false
begin
begin
Thread.current.send(kill_method_name)
ensure
raise "In dying thread"
end
ensure
yield
end
end
end
def self.join_dying_thread_with_outer_ensure(kill_method_name=:kill)
t = dying_thread_with_outer_ensure(kill_method_name) { yield }
-> { t.join }.should raise_error(RuntimeError, "In dying thread")
return t
end
def self.wakeup_dying_sleeping_thread(kill_method_name=:kill)
t = ThreadSpecs.dying_thread_ensures(kill_method_name) { yield }
Thread.pass while t.status and t.status != 'sleep'
t.wakeup
t.join
end
def self.critical_is_reset
# Create another thread to verify that it can call Thread.critical=
t = Thread.new do
initial_critical = Thread.critical
Thread.critical = true
Thread.critical = false
initial_critical == false && Thread.critical == false
end
v = t.value
t.join
v
end
def self.counter
@@counter
end
def self.counter= c
@@counter = c
end
def self.increment_counter(incr)
incr.times do
begin
Thread.critical = true
@@counter += 1
ensure
Thread.critical = false
end
end
end
def self.critical_thread1
Thread.critical = true
Thread.current.key?(:thread_specs).should == false
end
def self.critical_thread2(is_thread_stop)
Thread.current[:thread_specs].should == 101
Thread.critical.should == !is_thread_stop
unless is_thread_stop
Thread.critical = false
end
end
def self.main_thread1(critical_thread, is_thread_sleep, is_thread_stop)
# Thread.stop resets Thread.critical. Also, with native threads, the Thread.Stop may not have executed yet
# since the main thread will race with the critical thread
unless is_thread_stop
Thread.critical.should == true
end
critical_thread[:thread_specs] = 101
if is_thread_sleep or is_thread_stop
# Thread#wakeup calls are not queued up. So we need to ensure that the thread is sleeping before calling wakeup
Thread.pass while critical_thread.status and critical_thread.status != "sleep"
critical_thread.wakeup
end
end
def self.main_thread2(critical_thread)
Thread.pass # The join below seems to cause a deadlock with CRuby unless Thread.pass is called first
critical_thread.join
Thread.critical.should == false
end
def self.critical_thread_yields_to_main_thread(is_thread_sleep=false, is_thread_stop=false)
@@after_first_sleep = false
critical_thread = Thread.new do
Thread.pass while Thread.main.status and Thread.main.status != "sleep"
critical_thread1()
Thread.main.wakeup
yield
Thread.pass while @@after_first_sleep != true # Need to ensure that the next statement does not see the first sleep itself
Thread.pass while Thread.main.status and Thread.main.status != "sleep"
critical_thread2(is_thread_stop)
Thread.main.wakeup
end
sleep 5
@@after_first_sleep = true
main_thread1(critical_thread, is_thread_sleep, is_thread_stop)
sleep 5
main_thread2(critical_thread)
end
def self.create_critical_thread
Thread.new do
Thread.critical = true
yield
Thread.critical = false
end
end
def self.create_and_kill_critical_thread(pass_after_kill=false)
ThreadSpecs.create_critical_thread do
Thread.current.kill
Thread.pass if pass_after_kill
ScratchPad.record("status=" + Thread.current.status)
end
end
end