0

I'm trying to validate a basic yield/resume pattern with two Fibers. However, the yield/resume mechanism doesn't seem to work. I have tried a few variations but can't find my mistake.

Expected sequence of events:

  1. kick off threads (time 0)
  2. wait until first returns (time +2sec)
  3. wait until 2nd returns (time +2sec)
  4. done (time > +3sec)

Try #1

#!/usr/bin/env ruby

require 'fiber'

f1 = Fiber.new do
    puts "Fiber1 starting @ #{Time.new}."
    fib1 = Fiber.current
    Fiber.yield
    sleep 2
    puts "Fiber1 done @ #{Time.new}."
    fib1.resume(1)
end
f2 = Fiber.new do
    puts "Fiber2 starting @ #{Time.new}."
    fib2 = Fiber.current
    Fiber.yield
    sleep 2
    puts "Fiber2 done @ #{Time.new}."
    fib2.resume(2)
end

puts "Waiting @ #{Time.new}."
r1 = f1.resume
puts "f1 back @ #{Time.new} - #{r1}."
r2 = f2.resume
puts "f2 back @ #{Time.new} - #{r2}."

sleep 1
puts "Done @ #{Time.now}."

This results in:

Waiting @ 2016-06-01 06:15:52 -0700.
Fiber1 starting @ 2016-06-01 06:15:52 -0700.
f1 back @ 2016-06-01 06:15:52 -0700 - .
Fiber2 starting @ 2016-06-01 06:15:52 -0700.
f2 back @ 2016-06-01 06:15:52 -0700 - .
Done @ 2016-06-01 06:15:53 -0700.

Adding a second resume results in a FiberError.

Try #2

#!/usr/bin/env ruby

require 'fiber'

f1 = Fiber.new do
    puts "Fiber1 starting @ #{Time.new}."
    Fiber.yield
    sleep 2
    puts "Fiber1 done @ #{Time.new}."
    1
end
f2 = Fiber.new do
    puts "Fiber2 starting @ #{Time.new}."
    Fiber.yield
    sleep 2
    puts "Fiber2 done @ #{Time.new}."
    2
end

puts "Waiting @ #{Time.new}."
r1 = f1.resume
puts "f1 back @ #{Time.new} - #{r1}."
r2 = f2.resume
puts "f2 back @ #{Time.new} - #{r2}."

sleep 1
puts "Done @ #{Time.now}."

This results in:

Waiting @ 2016-06-01 10:53:17 -0700.
Fiber1 starting @ 2016-06-01 10:53:17 -0700.
f1 back @ 2016-06-01 10:53:17 -0700 - .
Fiber2 starting @ 2016-06-01 10:53:17 -0700.
f2 back @ 2016-06-01 10:53:17 -0700 - .
Done @ 2016-06-01 10:53:18 -0700.

In both cases, the start/end time is the same and result not returned.

saarp
  • 1,931
  • 1
  • 15
  • 28
  • Why are your fibers calling resume on themselves? – Frederick Cheung Jun 01 '16 at 17:20
  • @FrederickCheung - Tried it without the Fiber.current.resume. Still doesn't yield the expected result which is: 1) kick off threads, 2) wait until first returns, 3) wait until 2nd returns, 4) done. – saarp Jun 01 '16 at 18:21

1 Answers1

1

Fibers by themselves will not let you achieve parallelism, at least not without using some sort of callback mechanism such as eventmachine framework.

What you wrote is simply trying to interleave synchronous execution among code blocks. The reason you do not get expected sequence is because while you did simulate the kick-off, you never resumed the fibers after yeilding.

You might find the following post useful, particularly the example at the end:
http://schmurfy.github.io/2011/09/25/on_fibers_and_threads.html

Another example showing fibers transferring control to each other:
https://gist.github.com/aprescott/971008

This should give you expected results:

#!/usr/bin/env ruby

require 'fiber'

f1 = Fiber.new do
    puts "Fiber1 starting @ #{Time.new}."
    Fiber.yield
    sleep 2
    puts "Fiber1 done @ #{Time.new}."
    1
end
f2 = Fiber.new do
    puts "Fiber2 starting @ #{Time.new}."
    Fiber.yield
    sleep 2
    puts "Fiber2 done @ #{Time.new}."
    2
end

puts "Waiting @ #{Time.new}."
r1 = f1.resume
puts "f1 back @ #{Time.new} - #{r1}."
r2 = f2.resume
puts "f2 back @ #{Time.new} - #{r2}."

# Resume right after the yield in the fiber block and 
# execute until it encounters another yield or the block ends.
puts "Resuming f1"
f1.resume 
puts "Resuming f2"
f2.resume

sleep 1
puts "Done @ #{Time.now}."

Output:

Waiting @ 2016-06-05 00:35:29 -0700.
Fiber1 starting @ 2016-06-05 00:35:29 -0700.
f1 back @ 2016-06-05 00:35:29 -0700 - .
Fiber2 starting @ 2016-06-05 00:35:29 -0700.
f2 back @ 2016-06-05 00:35:29 -0700 - .
Resuming f1
Fiber1 done @ 2016-06-05 00:35:31 -0700.
Resuming f2
Fiber2 done @ 2016-06-05 00:35:33 -0700.
Done @ 2016-06-05 00:35:34 -0700.
vaibhav
  • 116
  • 2
  • 11
  • Thanks, @vaibhav. The 'sleep' is actually going to be replaced by call to two Defferable results that are serialized using EM::Synchrony.sync, so O think that should handle the parallel execution aspects. I still need to verify that. – saarp Jun 06 '16 at 20:41