Created
April 25, 2011 20:57
-
-
Save mrflip/941205 to your computer and use it in GitHub Desktop.
A fully-commented demo showing execution flow in the fibers+eventmachine => linear asynchronous code pattern
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>> load './RUNME-demo_of_async_fibers.rb' | |
/Users/flip/ics/backend/son_of_a_batch/RUNME-demo_of_async_fibers.rb:4: warning: already initialized constant FIBER_IDXS | |
814e5e90 0 f_0 beg main top level | |
814e5e90 1 f_0 beg em setup this block sets up the eventmachine execution, but no execution-order shenanigans yet | |
814e5e90 2 f_0 beg fiber stp the end-the-reactor block won't be called for 1.5s, so we get here immediately. | |
814e5e90 3 f_0 end fiber stp nothing from inside the Fiber.new{} block has run yet. | |
814e5e90 4 f_0 end fiber stp kick off the fiber... | |
80d829a0 5 f_1 beg fiber when my_fiber.resume is called, this runs. Now in a new fiber | |
80d829a0 6 f_1 beg get_result get_result is called from within fiber_1 | |
80d829a0 7 f_1 setup callback set up some code to run 1.5s from now | |
80d829a0 8 f_1 bef Fiber.yiel EM.add_timer just stashed the block away, so this runs next. | |
814e5e90 9 f_0 end em setup the line after my_fiber.resume, picks up when 'ret = Fiber.yield' is called. Now we go into the reactor loop and twiddle our thumbs for 1.5s | |
814e5e90 10 f_0 beg callbk Executed *by the main fiber* 1.5 seconds from now; all non-deferred code from the EventMachine.run{} setup block has happened | |
80d829a0 11 f_1 end get_result f.resume picks up here when the timer goes off. | |
80d829a0 12 f_1 got it: 'bob' the line after get_result wast called -- this isn't run until f.resume(:bob) from inside the method. | |
80d829a0 13 f_1 end fiber the get_result function returned; from here, picks up at the line following 'f.resume(:bob)' | |
814e5e90 14 f_0 end callbk We come back here from the *end of the fiber*: we're now back in the root fiber for good. | |
814e5e90 15 f_0 stop runner this will be the *next to last* thing in the program... 2.5s into the future, stop the Eventmachine reactor | |
814e5e90 16 f_0 end main done. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'fiber' | |
require 'eventmachine' | |
# | |
# don't worry about this code at the top. It's ugly code to make pretty log lines. | |
# Start reading in the eventmachine block at bottom, then the method 'get_results'. | |
# | |
FIBER_IDXS = {} | |
def fiber_idx(); FIBER_IDXS[Fiber.current.object_id] ||= FIBER_IDXS.length ; end | |
$order = 0 | |
def ll indent, where, guess, *desc | |
info = "#{" "*indent} #{where}" | |
puts "%8s %3d\t%-15s\t%-7s\t%-30s\t%-30s\t%s" % [ Fiber.current.object_id.to_s(16), $order, guess, fiber_idx, *(fiber_idx == 0 ? [info, " "*30] : [" "*30, info]), desc.join("\t") ] | |
$order += 1 | |
end | |
# | |
# Start reading in the eventmachine block below, then come back here | |
# | |
def get_result() | |
ll(4, 'beg get_result', " 6 fiber_1", "get_result is called from within fiber_1") | |
f = Fiber.current | |
ll(4, 'setup callback', " 7 fiber_1", "set up some code to run 1.5s from now") | |
EM.add_timer(1.5) do | |
ll(5, 'beg callback', "10 fiber_0", "Executed *by the main fiber* 1.5 seconds from now; all non-deferred code from the EventMachine.run{} setup block has happened") | |
f.resume(:bob) # fiber_0 -- Ha! pick up where the fiber left off, the 'ret = Fiber.yield' line; 'ret' will get the value ':bob' | |
ll(5, 'end callback', "14 fiber_0", "We come back here from the *end of the fiber*: we're now back in the root fiber for good.") | |
end | |
ll(4, 'bef Fiber.yield', " 8 fiber_1", "EM.add_timer just stashed the block away, so this runs next.") | |
ret = Fiber.yield # fiber_1 -- Watchout! this suspends action. | |
ll(4, 'end get_result', "11 fiber_1", "f.resume picks up here when the timer goes off.") | |
return ret | |
end | |
# | |
# Start here: | |
# | |
ll(0, 'beg main', " 0 fiber_0", "top level") | |
EventMachine.run do | |
ll(1, 'beg em setup', " 1 fiber_0", "this block sets up the eventmachine execution, but no execution-order shenanigans yet") | |
EM.add_timer(2.5){ | |
ll(2, "stop runner", "15 fiber_0", "this will be the *next to last* thing in the program... 2.5s into the future, stop the Eventmachine reactor") | |
EM.stop | |
} | |
ll(2, 'beg fiber setup', " 2 fiber_0", "the end-the-reactor block won't be called for 1.5s, so we get here immediately.") | |
my_fiber = Fiber.new{ | |
ll(3, 'beg fiber', " 5 fiber_1", "when my_fiber.resume is called, this runs. Now in a new fiber") | |
res = get_result() # fiber1 -- get_result is called, but the last thing it does is pause execution | |
ll(3, "got it: '#{res}'", "12 fiber_1", "the line after get_result wast called -- this isn't run until f.resume(:bob) from inside the method.") | |
ll(3, "end fiber", "13 fiber_1", "the get_result function returned; from here, picks up at the line following 'f.resume(:bob)'") | |
} | |
ll(2, 'end fiber setup', " 3 fiber_0", "nothing from inside the Fiber.new{} block has run yet.") | |
ll(2, 'end fiber setup', " 4 fiber_0", "kick off the fiber...") | |
my_fiber.resume | |
ll(1, 'end em setup', " 9 fiber_0", "the line after my_fiber.resume, picks up when 'ret = Fiber.yield' is called. Now we go into the reactor loop and twiddle our thumbs for 1.5s") | |
end | |
ll(0, 'end main', "16 fiber_0", "done.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment