Skip to content

Instantly share code, notes, and snippets.

@nevans
Last active October 27, 2020 05:03
Show Gist options
  • Save nevans/4d9a0e591e06ced2e2691a363452da9b to your computer and use it in GitHub Desktop.
Save nevans/4d9a0e591e06ced2e2691a363452da9b to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
require "fiber" # needed for transfer
RootFiberException = Class.new(Exception)
RootFiberTestSignal = Class.new(RootFiberException)
# Warning: this is only safe to call from the root fiber for a thread.
# Unfortunately, your code may not know if it is in the root fiber. This will
# raise an exception in the root fiber of the current thread. If you are in the
# root fiber, it will also catch that exception. If not, the root fiber will
# most likely crash, bringing the thread down with it!
#
# The trick is that, unlike Fiber#resume, Fiber#transfer doesn't set a
# "return_fiber" pointer. So when the fiber returns (or yields or raises), it
# doesn't necessarily go back the the fiber that created it or the fiber that
# transfered into it. Instead, it transfers to the root fiber.
def check_in_root_fiber!
msg = "Another fiber raised this exception (#{rand})"
test_fiber = Fiber.new { raise RootFiberTestSignal, msg; }
test_fiber.transfer # th
rescue RootFiberTestSignal => ex
raise ex unless ex.message == msg
true
else
raise RootFiberException, "in_root_fiber? was called from a non-root fiber"
end
check_in_root_fiber! # => true
nonroot = Fiber.new do
Fiber.new do
Fiber.new do
Fiber.new do
check_in_root_fiber!
end.resume
end.resume
end.resume
end
nonroot.resume
# ~> RootFiberTestSignal
# ~> Another fiber raised this exception (0.06201568006087321)
# ~>
# ~> /tmp/seeing_is_believing_temp_dir20201027-2846277-176v8hz/program.rb:18:in `block in check_in_root_fiber!'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment