threads/raising_into_threads_parens.rb
tests/_examples/threads/raising_into_threads_parens.rb in the metorex repository.
# `Thread#raise` builds the exception the way `raise` does, in the thread that
# asks, and the other thread raises it where it stands. On the thread running
# now it raises at once, and a thread that has ended takes nothing.
def refusal()
yield()
rescue StandardError => problem
[problem.class(), problem.message()]
end
p(refusal() { Thread.current().raise(ArgumentError, "at once") })
p(refusal() { Thread.current().raise(Object.new()) })
done = Thread.new() { :over }
done.join()
p(done.raise("late"))
# The cause is settled where the exception is built, from what that thread
# was handling, rather than from what the raising thread was handling.
caught = nil
sleeper = Thread.new() do
Thread.current().report_on_exception = false
begin
begin
raise("inside")
rescue
sleep()
end
rescue => caught
end
end
Thread.pass() until sleeper.stop?()
p($!)
sleeper.raise("handed over")
sleeper.join()
p(caught.message())
p(caught.cause())
# An exception handed with a message is asked for a copy through its own
# `exception`.
logged = []
tracked = Class.new(StandardError) do
define_method(:exception) do |*given|
logged << given
super(*given)
end
end
waiting = Thread.new() do
Thread.current().report_on_exception = false
sleep()
end
Thread.pass() until waiting.stop?()
waiting.raise(tracked.new(), "with a message")
p(refusal() { waiting.join() }[1])
p(logged.first(2))
# `rescue => name` inside a block sets the variable the block can see.
seen = nil
[1].each() do
begin
raise("in a block")
rescue => seen
end
end
p(seen.message())
# Each fiber keeps its own `$!`.
begin
raise("outer")
rescue
fiber = Fiber.new() { $! }
p(fiber.resume())
p($!.message())
end
Output:
[ArgumentError, "at once"]
[TypeError, "exception class/object expected"]
nil
nil
"handed over"
nil
"with a message"
[["with a message"], []]
"in a block"
nil
"outer"