Fiber.yield switching issue

Please check following code.

ch = Channel(UInt128).new
terminate = Channel(Nil).new
done = Channel(Nil).new

[1, 2, 3].each do |i|
  spawn do
    x = 0_u128
    y = rand(5000000..10000000)
    (1..y).each { |e| x += e }
    Fiber.yield
    puts i.to_s * 50
  end
end

spawn do
  loop do
    select
    when value = ch.receive
      p value
    when terminate.receive?
      break
    end
  end
  done.close
  puts "exiting loop fiber"
end

terminate.close
done.receive?
puts "back to main fiber"
Fiber.yield
puts "end"

It output like following run with 1.14.0

exiting loop fiber
back to main fiber
11111111111111111111111111111111111111111111111111
22222222222222222222222222222222222222222222222222
end

Why it not output like

11111111111111111111111111111111111111111111111111
22222222222222222222222222222222222222222222222222
33333333333333333333333333333333333333333333333333

Or

11111111111111111111111111111111111111111111111111

Thanks.

I can reproduce it in WSL.
But when I run it on Windows, it performs diffierently. :thinking:

exiting loop fiber
back to main fiber
11111111111111111111111111111111111111111111111111
22222222222222222222222222222222222222222222222222
33333333333333333333333333333333333333333333333333
end

Oops, forget to mention, i run it on Arch linux.

But when I run it on Windows, it performs diffierently. :thinking:

AFAIU, Your result is expected.

Spawning fibers doesn’t tell when an how the fibers will run. Using a trace on linux and the current scheduler & event loop, the program eventually enqueues the fibers in this order:

  • the first 2 fiber (111 and 222)
  • the main fiber
  • the 3rd fiber (333)

The current scheduler resumes fibers in the order they were enqueued, so you get 111 then 222 then the main fiber resumes and exits, and 333 is never resumed.

The difference between windows and linux is… the eventloop implementation. They don’t behave the same yet are correct: they enqueued the 4 fibers that yield (ready to be resumed).

TLDR: fibers can run in whatever order at whatever time and may never be resumed (as is the case here) or even started. You need synchronization or to verify that they actually ran before exiting. I.e. you want WaitGroup.wait.

You can’t expect to print 111 then 222 then 333 in that specific order, or to even be printed at all, it will only happen by sheer luck.

1 Like

I saw this result too.

Really awesome answer need add into our crystal-book.

Thanks a lot!

There’s a section for it already, at least the basics: Runtime Tracing - Crystal

Oh, thanks, i means the answer for this question, not how to use tracing.

e.g.

Spawning fibers doesn’t tell when and how the fibers will run

The difference between windows and linux is… the eventloop implementation. They don’t behave the same yet are correct

fibers can run in whatever order at whatever time and may never be resumed (as is the case here) or even started. You need synchronization or to verify that they actually ran before exiting.

You can’t expect to print in that specific order, or to even be printed at all, it will only happen by sheer luck.