DEV Community

hungle00
hungle00

Posted on • Edited on

Ruby Fiber Scheduler and Async

Ruby 3 comes with a lot of support tools for concurrency programming, one of them is Fiber Scheduler. In this post, I'll introduce briefly about Fiber Scheduler, the Async gem, and then, implement the simple server using Fiber Scheduler.

What is Fiber

Fiber can be seen as a lightweight thread or thread implemented at the programming language level instead of the OS level.

fiber = Fiber.new do
  #...
end
fiber.resume # transfer / Fiber.schedule
Enter fullscreen mode Exit fullscreen mode

A Fiber is a lightweight unit of execution that can be suspended and resumed at specific points. Because only one fiber can execute at a time, they are often referred to as a mechanism for cooperative concurrency. Theoretically, the advantage of Fibers is less context switching compared to Thread, but before Ruby 3, Fibers lacked the scheduler implementation to be useful.

Fiber scheduler

Since Ruby 3, Fibers have been given superpowers in the form of the FiberScheduler.
The Fiber Scheduler consists of two parts:

  • Fiber Scheduler interface
  • Fiber Scheduler implementation

What Ruby 3 implements is the interface. It would not use the scheduler unless a scheduler implementation is included.
If you want to enable the asynchronous behavior in Ruby, you need to set a Fiber Scheduler object.

Fiber.set_scheduler(scheduler)
Enter fullscreen mode Exit fullscreen mode

The list of Fiber Scheduler implementations and their main differences can be found at Fiber Scheduler List project.
I recommend you read this article to understand more detail about Fiber Scheduler.

Async gem:

One of the most mature and common Fiber Scheduler implementations is by Samuel Williams. Furthermore, he not only implemented a Fiber Scheduler but created the gem called Async which was more powerful for working with asynchronous programming.

If you want to go to details about the Async gem and how to use it, you should read the document Async Guides

The simple use of async gem is quite straightforward, you just need to wrap the computation code in Kernel#Async method, for example:

require 'async'

Async do |task|
  puts "Hello World!"
end
Enter fullscreen mode Exit fullscreen mode

Simple asynchronous HTTP server

In this part, I'll implement the simple HTTP server using Async gem.
For simplicity, I will get the code from Appsignal's article Building a 30 line HTTP server in Ruby. After that, add the async gem to make the code asynchronous.

Using Fiber syntax + Async::Scheduler

require 'socket'
require 'async/scheduler'

Fiber.set_scheduler(Async::Scheduler.new)
server = TCPServer.new 2000 # Server bound to port 2000

app = Proc.new do
  ['200', {'Content-Type' => 'text/html'}, ["Hello world! The time is #{Time.now}"]]
end

Fiber.schedule do
  loop do
    session = server.accept    # Wait for a client to connect
    Fiber.schedule do
      status, headers, body = app.call({})

      session.print "HTTP/1.1 #{status}\r\n"

      headers.each do |key, value|
        session.print "#{key}: #{value}\r\n"
      end

      session.print "\r\n"

      body.each do |part|
        session.print part
      end

      session.close
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

or using Kernel#Async method

require 'socket'
require 'async'

server = TCPServer.new 2000

app = Proc.new do
  ['200', {'Content-Type' => 'text/html'}, ["Hello world! The time is #{Time.now}"]]
end

Async do
  loop do
    session = server.accept    # Wait for a client to connect
    Async do
      status, headers, body = app.call({})

      # ... same code as above
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Our server is very simple but it is enough to demonstrate the usage and the advantage of Async gem. You can check the full code, and benchmark testing on [this repo].(https://github.com/hungle00/async-http-server)

If you wonder about the Ruby asynchronous server that can be used in production, you can check this gem, it is built on top of the Async gem.

Top comments (0)