Oguz Bilgic
Oguz Bilgic

Reputation: 3480

Cycling between Fibers with no IO

As far as I know, crystal cycles Fibers with io, meaning that if one fiber is waiting for io, crystal will switch to an another fiber.

What if we spawn two fibers but one of them does constant computation/loop with no io?

For example, with the code below server doesn't respond to any http requests

spawn do
  Kemal.run
end

spawn do
  # constant computation/loop with no IO
  some_func
end

Fiber.yield
# or sleep

Upvotes: 5

Views: 295

Answers (1)

Philipp Claßen
Philipp Claßen

Reputation: 44009

By default, Crystal uses cooperative multitasking. To implement it, the Crystal runtime provides Fibers. Due to their cooperative nature, you have to yield execution from time to time (e.g. with Fibers.yield):

Fibers are cooperative. That means execution can only be drawn from a fiber when it offers it. It can't be interrupted in its execution at random. In order to make concurrency work, fibers must make sure to occasionally provide hooks for the scheduler to swap in other fibers. [...]

When a computation-intensive task has none or only rare IO operations, a fiber should explicitly offer to yield execution from time to time using Fiber.yield to break up tight loops. The frequency of this call depends on the application and concurrency model.

Note that CPU intense operations are not the only source for starving other Fibers. When calling C libraries that may block, the Fiber will also wait the operation to complete. An example would be a long-polling operation, which will wait for the next event or eventually time out (e.g. rd_kafka_poll in Kafka). To prevent that, prefer async API version (if available), or use a short polling interval (e.g. 0 for Kafka poll) and shift the sleep operation to the Crystal runtime, so the other Fibers can run.

In 2019, Crystal introduced support for parallelism. By running multiple worker threads, you can also prevent one expensive computation for starving all other operations. However, you have to be careful as the responsiveness (and maybe even correctness) of the program could then depend on the number of workers (e.g. with only one worker, it will still hang). Overall, yielding occasionally in time-extensive operations seems to be the better solution, even if you end up using multiple workers for the improved performance on multi-core machines.

Upvotes: 1

Related Questions