# Why fiber scheduler work different when switch fibers use Channel#send OR Fiber.yield?

**URL:** https://forum.crystal-lang.org/t/why-fiber-scheduler-work-different-when-switch-fibers-use-channel-send-or-fiber-yield/7323
**Category:** Help & Support
**Created:** [October 19, 2024, 7:28pm UTC](https://forum.crystal-lang.org/t/why-fiber-scheduler-work-different-when-switch-fibers-use-channel-send-or-fiber-yield/7323 "2024-10-19T19:28:43Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![zw963](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/zw963/32/1623_2.png) [@zw963](https://forum.crystal-lang.org/u/zw963)
#### Post date: [October 19, 2024, 7:28pm UTC](https://forum.crystal-lang.org/t/why-fiber-scheduler-work-different-when-switch-fibers-use-channel-send-or-fiber-yield/7323/1 "2024-10-19T19:28:43Z")

</div>

Following is a example.

```crystal
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)
    puts "6"*50
    (1..y).each { |e| x += e }
    puts "7"*50
    ch.send x
    puts "8"*50
  end
end

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

puts "1"*50
terminate.close
puts "2"*50
done.receive?
puts "end"

```

When i run above code, i get result like following:

```auto
11111111111111111111111111111111111111111111111111
22222222222222222222222222222222222222222222222222

66666666666666666666666666666666666666666666666666
77777777777777777777777777777777777777777777777777
66666666666666666666666666666666666666666666666666
77777777777777777777777777777777777777777777777777
66666666666666666666666666666666666666666666666666
77777777777777777777777777777777777777777777777777

44444444444444444444444444444444444444444444444444
31855631199211
44444444444444444444444444444444444444444444444444
45854814984355
44444444444444444444444444444444444444444444444444
15626260923210

33333333333333333333333333333333333333333333333333
exiting loop fiber

88888888888888888888888888888888888888888888888888
88888888888888888888888888888888888888888888888888
88888888888888888888888888888888888888888888888888

end

```

If I change the 20th line of code from `ch.send x` into `Fiber.yield`, like following:

```crystal
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)
    puts "6"*50
    (1..y).each { |e| x += e }
    puts "7"*50
    Fiber.yield
    puts "8"*50
  end
end

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

puts "1"*50
terminate.close
puts "2"*50
done.receive?
puts "end"

```

I got new output like following:

```auto
11111111111111111111111111111111111111111111111111
22222222222222222222222222222222222222222222222222

66666666666666666666666666666666666666666666666666
77777777777777777777777777777777777777777777777777
66666666666666666666666666666666666666666666666666
77777777777777777777777777777777777777777777777777
66666666666666666666666666666666666666666666666666
77777777777777777777777777777777777777777777777777

33333333333333333333333333333333333333333333333333
exiting loop fiber

end

```

My question is, why the first code output following `888888`

```auto
88888888888888888888888888888888888888888888888888
88888888888888888888888888888888888888888888888888
88888888888888888888888888888888888888888888888888

```

but it does not for the second code?

the first code scheduler switch to the `blocked fibers in the each` before `loop's fiber` exit so `puts "8"*50`, but, it’s does not happen for second code.

What are the differences when fiber scheduler works for above two code?

Before switch back to the `done.receive` in the main fiber, main fiber will waiting for all spawned fiber to be fulled blocked/exit or not? above two codes get opposite result, causing me very confused.

Test on 1.14.0, thanks

* * *

**EDIT:**

If replace `ch.send x` with `sleep 1` or `sleep 0`, the result same as `Fiber.yield`, only `ch.send x` behavior different.

The only explanation I can think of is that Fiber scheduler will tries switch to fibers which blocked by channel operation (e.g. `ch.send`) and execute them before `main fiber` exit, but this is not the case when used with `Fiber.yield/sleep 0/sleep 1`?

---

<div class="post-metadata">

### Author: ![Fahrkunst](https://avatars.discourse-cdn.com/v4/letter/f/a183cd/32.png) [@Fahrkunst](https://forum.crystal-lang.org/u/Fahrkunst)
#### Post date: [October 20, 2024, 8:17am UTC](https://forum.crystal-lang.org/t/why-fiber-scheduler-work-different-when-switch-fibers-use-channel-send-or-fiber-yield/7323/2 "2024-10-20T08:17:59Z")

</div>

The problem is not the scheduler. The sub fibers gets called once only. The yield and sleep version miss a way to reenter/continue the paused fibers.

---

<div class="post-metadata">

### Author: ![zw963](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/zw963/32/1623_2.png) [@zw963](https://forum.crystal-lang.org/u/zw963)
#### Post date: [October 20, 2024, 8:35am UTC](https://forum.crystal-lang.org/t/why-fiber-scheduler-work-different-when-switch-fibers-use-channel-send-or-fiber-yield/7323/3 "2024-10-20T08:35:54Z")

</div>

> [@Fahrkunst](#):
>
> The yield and sleep version miss a way to reenter/continue the paused fibers.

Thanks for the answer, so, only operation on the channel can reenter/continue the paused fiber, right? another way to say it, an unbuffered channel will be executed before the main fiber?

Is this true for `sleep 1` too? i means, `sleep 1` can explain as: `i want to rest a second, come back later`, it expected to back later.

---

<div class="post-metadata">

### Author: ![Fahrkunst](https://avatars.discourse-cdn.com/v4/letter/f/a183cd/32.png) [@Fahrkunst](https://forum.crystal-lang.org/u/Fahrkunst)
#### Post date: [October 20, 2024, 8:42am UTC](https://forum.crystal-lang.org/t/why-fiber-scheduler-work-different-when-switch-fibers-use-channel-send-or-fiber-yield/7323/4 "2024-10-20T08:42:38Z")

</div>

It is not the only way: inserting yields in your code

```auto
done.receive?
Fiber.yield
Fiber.yield
Fiber.yield
puts "end"

```

reenters the paused fibers

---

<div class="post-metadata">

### Author: ![zw963](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/zw963/32/1623_2.png) [@zw963](https://forum.crystal-lang.org/u/zw963)
#### Post date: [October 20, 2024, 9:01am UTC](https://forum.crystal-lang.org/t/why-fiber-scheduler-work-different-when-switch-fibers-use-channel-send-or-fiber-yield/7323/5 "2024-10-20T09:01:03Z")

</div>

> It is not the only way: inserting yields in your code

Thanks, i consider this is another story, I want to confirm if there is a more reasonable explanation (or doc) to explain the difference behavior between Fiber.yield/sleep and channel operation, that is, why `ch.send x` version works even even without need `Fiber.yield`?

---

<div class="post-metadata">

### Author: ![zw963](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/zw963/32/1623_2.png) [@zw963](https://forum.crystal-lang.org/u/zw963)
#### Post date: [October 20, 2024, 9:04am UTC](https://forum.crystal-lang.org/t/why-fiber-scheduler-work-different-when-switch-fibers-use-channel-send-or-fiber-yield/7323/6 "2024-10-20T09:04:32Z")

</div>

> [@Fahrkunst](#):
>
> ```auto
> done.receive?
> Fiber.yield
> Fiber.yield
> Fiber.yield
> puts "end"
> 
> ```

BTW: i tested with two Fiber.yield added, it works same as three.

But i thought it should work same way if only one Fiber.yield added, but the result is only output `88888` twice instead expected 3 times, could you explain why?

```crystal
done.receive?
Fiber.yield
puts "end"

```

Thanks

---

<div class="post-metadata">

### Author: ![Fahrkunst](https://avatars.discourse-cdn.com/v4/letter/f/a183cd/32.png) [@Fahrkunst](https://forum.crystal-lang.org/u/Fahrkunst)
#### Post date: [October 20, 2024, 9:08am UTC](https://forum.crystal-lang.org/t/why-fiber-scheduler-work-different-when-switch-fibers-use-channel-send-or-fiber-yield/7323/7 "2024-10-20T09:08:59Z")

</div>

with Fiber.yield you order scheduler to switch.  
with sleep you pause the fiber and the scheduler will look wether there are other fibers waiting = ready to go on.  
with send you mark data to be worked on elsewhere but continue in that fiber.

---

<div class="post-metadata">

### Author: ![zw963](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/zw963/32/1623_2.png) [@zw963](https://forum.crystal-lang.org/u/zw963)
#### Post date: [October 26, 2024, 6:06pm UTC](https://forum.crystal-lang.org/t/why-fiber-scheduler-work-different-when-switch-fibers-use-channel-send-or-fiber-yield/7323/8 "2024-10-26T18:06:07Z")

</div>

After check scheduler tracing log, it become more clearly for now, as original answer supplement:

with Fiber.yield you force switch, manually.

with sleep or Channel operation, just activate rescheduler event, whether switch or not, or switch to which fiber, need re-calculate, because both of them affect orders. e.g. `ch.receive` probably add the switched out fiber back, sleep time different will affect fiber order.

There are many things that can be said, but, limited by English is not my native language, it’s difficult to express.
