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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
|
unless defined? Channel
require 'thread'
class Channel < Queue
alias receive shift
end
end
module ThreadSpecs
class SubThread < Thread
def initialize(*args)
super { args.first << 1 }
end
end
class Status
attr_reader :thread, :inspect, :status
def initialize(thread)
@thread = thread
@alive = thread.alive?
@inspect = thread.inspect
@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_aborting_thread
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 { 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.dying_thread_ensures(kill_method_name=:kill)
t = Thread.new do
begin
Thread.current.send(kill_method_name)
ensure
yield
end
end
end
def self.dying_thread_with_outer_ensure(kill_method_name=:kill)
t = Thread.new do
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 }
lambda { 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(isThreadStop)
Thread.current[:thread_specs].should == 101
Thread.critical.should == !isThreadStop
if not isThreadStop
Thread.critical = false
end
end
def self.main_thread1(critical_thread, isThreadSleep, isThreadStop)
# 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
if not isThreadStop
Thread.critical.should == true
end
critical_thread[:thread_specs] = 101
if isThreadSleep or isThreadStop
# 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(isThreadSleep=false, isThreadStop=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(isThreadStop)
Thread.main.wakeup
end
sleep 5
@@after_first_sleep = true
main_thread1(critical_thread, isThreadSleep, isThreadStop)
sleep 5
main_thread2(critical_thread)
end
def self.create_critical_thread()
critical_thread = Thread.new do
Thread.critical = true
yield
Thread.critical = false
end
return critical_thread
end
def self.create_and_kill_critical_thread(passAfterKill=false)
critical_thread = ThreadSpecs.create_critical_thread do
Thread.current.kill
if passAfterKill
Thread.pass
end
ScratchPad.record("status=" + Thread.current.status)
end
end
end
|