# Fiber.yield

**URL:** <https://forum.crystal-lang.org/t/fiber-yield/6533>\
**Category:** Help & Support\
**Created:** [February 6, 2024, 11:49am UTC](https://forum.crystal-lang.org/t/fiber-yield/6533 "2024-02-06T11:49:34Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![lemmuh](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/lemmuh/32/2236_2.png) [@lemmuh](https://forum.crystal-lang.org/u/lemmuh)\
**Post date:** [February 6, 2024, 11:49am UTC](https://forum.crystal-lang.org/t/fiber-yield/6533/1 "2024-02-06T11:49:35Z")

</div>

In my app I have a http-server fiber and a worker fiber.

I observed under heavy worker load (reading/parsing/writing files within a loop), that the http server is not responding. Right after the worker fiber is finished with its job, the http server responds.

I aware of the possibility to pass the control back to the fiber scheduler with `Fiber.yield`.

I inserted the `Fiber.yield` method in the worker loop, however the situation didn’t change. Still the http server responded after the worker job.

How does the fiber scheduler decides which fiber is going to be the next to run ?

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [February 6, 2024, 2:20pm UTC](https://forum.crystal-lang.org/t/fiber-yield/6533/2 "2024-02-06T14:20:21Z")

</div>

Do you have some example code? Otherwise best I can guess at the moment is that the worker fiber is doing something that is preventing execution from switching back to the main fiber that is running HTTP server. Which Is a bit interesting to me if you say it’s doing file operations as normally I would have thought the scheduler would process other fibers while waiting on IO when reading from the files. But it’s kinda hard to say for sure without some code to look at.

---

<div class="post-metadata">

**Author:** ![nogginly](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/nogginly/32/1795_2.png) [@nogginly](https://forum.crystal-lang.org/u/nogginly)\
**Post date:** [February 8, 2024, 3:58am UTC](https://forum.crystal-lang.org/t/fiber-yield/6533/3 "2024-02-08T03:58:33Z")

</div>

Perhaps obvious question: are you compiling with `-Dpreview_mt` to enable multi-threading support for fibres?

---

<div class="post-metadata">

**Author:** ![lemmuh](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/lemmuh/32/2236_2.png) [@lemmuh](https://forum.crystal-lang.org/u/lemmuh)\
**Post date:** [February 8, 2024, 8:20am UTC](https://forum.crystal-lang.org/t/fiber-yield/6533/4 "2024-02-08T08:20:04Z")

</div>

No. I thought even when I don’t using multi-threading the fiber scheduler should/can switch to another fiber while waiting in IO operations.

What I forgot to mention in my first post, I have to use windows ? Is multi-threading also supported on windows ?

---

<div class="post-metadata">

**Author:** ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)\
**Post date:** [February 8, 2024, 10:42am UTC](https://forum.crystal-lang.org/t/fiber-yield/6533/5 "2024-02-08T10:42:31Z")

</div>

> [@lemmuh](#):
>
> Still the http server responded after the worker job.

What exactly does this mean?  
Could you maybe share some code to show what you’re actually doing.

This should all work as you expect. Concurrency is fully supported on Windows.

---

<div class="post-metadata">

**Author:** ![lemmuh](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/lemmuh/32/2236_2.png) [@lemmuh](https://forum.crystal-lang.org/u/lemmuh)\
**Post date:** [February 8, 2024, 4:46pm UTC](https://forum.crystal-lang.org/t/fiber-yield/6533/6 "2024-02-08T16:46:57Z")

</div>

My app has in principle this structure:

```auto
start_channel = Channel(Nil).new(1)

spawn name: "http_server" do
  HTTP::Server.new do |context|
    case context.request.path
    when /^start$/
      start_channel.send nil
      context.response.status = HTTP::Status::OK
      context.response.content_type = "text/json"
      context.response << data.to_json
    else
      context.response.respond_with_status HTTP::Status::NOT_FOUND
    end
  end
end

spawn name: "worker" do
   loop do
     select
     when start_channel.receive
        io = IO::Memory.new
        get_big_file_list.each do |file|
           Compress::Zip::Writer.open(io) do |writer|
             writer.add_file File.basename(file), File.read(file)
           end
           ...
        end
        ...
     end
   end
end

sleep 

```

---

<div class="post-metadata">

**Author:** ![jgaskins](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/jgaskins/32/2449_2.png) [@jgaskins](https://forum.crystal-lang.org/u/jgaskins)\
**Post date:** [February 8, 2024, 6:35pm UTC](https://forum.crystal-lang.org/t/fiber-yield/6533/7 "2024-02-08T18:35:41Z")

</div>

> [@lemmuh](#):
>
> the fiber scheduler should/can switch to another fiber while waiting in IO operations.

Sounds like a case of not all I/O operations being the same. File I/O isn’t blocking in the same way that socket I/O is. Blocking occurs when there isn’t data ready, but this is never the case for local files. Round trips to disk are often measured in nanoseconds so the CPU is never yielded.

This can even be the case for sockets when data is coming in over the wire as fast as or faster than you’re processing it. Since reading from a `TCPSocket` is really reading from a buffer in memory, if every `TCPSocket#read` requests less data than you currently have in the buffer (keeping in mind that the kernel also has its own socket buffers in addition Crystal `IO::Buffered` ones), you’re never _actually_ waiting on the socket, so in some scenarios you never yield the CPU in socket I/O, either.

---

<div class="post-metadata">

**Author:** ![lemmuh](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/lemmuh/32/2236_2.png) [@lemmuh](https://forum.crystal-lang.org/u/lemmuh)\
**Post date:** [February 9, 2024, 10:56am UTC](https://forum.crystal-lang.org/t/fiber-yield/6533/8 "2024-02-09T10:56:51Z")

</div>

I understand. Thus I have to use the multi-threading option in order to keep the http server reachable while the worker fiber is doing its job.

---

<div class="post-metadata">

**Author:** ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)\
**Post date:** [February 9, 2024, 11:09am UTC](https://forum.crystal-lang.org/t/fiber-yield/6533/9 "2024-02-09T11:09:34Z")

</div>

Parallelism would mostly improve throughput of your CPU-bound worker tasks. That might be meaningful if you need to run multiple workloads in parallel.  
But multithreading is absolutely _not_ necessary to have a snappy response from the HTTP server. Server and worker fiber should be perfectly able to coordinate sharing CPU time in single-thread concurrency.  
If there are no reschedule points in a long-running tasks, that may require some strategically placed `Fiber.yield` calls to implement cooperative sharing.

You mentioned that you did already try that. Where did you put `Fiber.yield` in that example?

---

<div class="post-metadata">

**Author:** ![lemmuh](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/lemmuh/32/2236_2.png) [@lemmuh](https://forum.crystal-lang.org/u/lemmuh)\
**Post date:** [February 9, 2024, 11:59am UTC](https://forum.crystal-lang.org/t/fiber-yield/6533/10 "2024-02-09T11:59:26Z")

</div>

As the last line in the `get_big_file_list.each` block.

---

<div class="post-metadata">

**Author:** ![jgaskins](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/jgaskins/32/2449_2.png) [@jgaskins](https://forum.crystal-lang.org/u/jgaskins)\
**Post date:** [February 9, 2024, 11:46pm UTC](https://forum.crystal-lang.org/t/fiber-yield/6533/11 "2024-02-09T23:46:26Z")

</div>

`Fiber.yield` will ensure that your other fibers get a chance to run — it literally pushes the current fiber onto the end of the scheduler’s queue. Since we don’t see all of the code in your worker fiber, and since you’re putting explicit `Fiber.yield` calls into the loop, it sounds like what’s hogging the CPU might be elsewhere.

- If your first thought was that you were getting stuck on file I/O, it sounds like you’re shoveling a _lot_ of data into that `IO::Memory` instance. That would probably consume a lot of RAM, so it might even be worth checking whether you’re dipping into swap.
- Is it possible it’s getting past that code and the lines you’ve omitted are what’s actually CPU-bound?
- Are you doing anything with the `IO::Memory` buffer after all the files have been shoveled into it that could be CPU-bound?
- Does that `select` statement have an `else` in the actual code? If so, it won’t block at all and will basically just be executing `loop { }`, which will _definitely_ not let your HTTP server work until it comes upon something that yields the CPU. It might be worth sticking a `Fiber.yield` just inside the `loop` block, as well or, if the `select` does contain an `else` clause, replace it with `when timeout(1.second)` to long-poll the `start_channel`.
