Skip to content
On this page

Maintenance Ruby 3.4 gets fixes, but no new features. Switch to Ruby 4.0 →

class · Ruby 3.4

Fiber

< Object 12 instance methods 11 class methods

Fibers are primitives for implementing light weight cooperative concurrency in Ruby. Basically they are a means of creating code blocks that can be paused and resumed, much like threads. The main difference is that they are never preempted and that the scheduling must be done by the programmer and not the VM.

As opposed to other stackless light weight concurrency models, each fiber comes with a stack. This enables the fiber to be paused from deeply nested function calls within the fiber block. See the ruby(1) manpage to configure the size of the fiber stack(s).

When a fiber is created it will not run automatically. Rather it must be explicitly asked to run using the Fiber#resume method. The code running inside the fiber can give up control by calling Fiber.yield in which case it yields control back to caller (the caller of the Fiber#resume).

Upon yielding or termination the Fiber returns the value of the last executed expression

For instance:

Example
fiber = Fiber.new do
  Fiber.yield 1
  2
end

puts fiber.resume
puts fiber.resume
puts fiber.resume

produces

1
2
FiberError: dead fiber called

The Fiber#resume method accepts an arbitrary number of parameters, if it is the first call to resume then they will be passed as block arguments. Otherwise they will be the return value of the call to Fiber.yield

Example:

Example
fiber = Fiber.new do |first|
  second = Fiber.yield first + 2
end

puts fiber.resume 10
puts fiber.resume 1_000_000
puts fiber.resume "The fiber will be dead before I can cause trouble"

produces

12
1000000
FiberError: dead fiber called

Non-blocking Fibers

The concept of non-blocking fiber was introduced in Ruby 3.0. A non-blocking fiber, when reaching a operation that would normally block the fiber (like sleep, or wait for another process or I/O) will yield control to other fibers and allow the scheduler to handle blocking and waking up (resuming) this fiber when it can proceed.

For a Fiber to behave as non-blocking, it need to be created in Fiber.new with blocking: false (which is the default), and Fiber.scheduler should be set with Fiber.set_scheduler. If Fiber.scheduler is not set in the current thread, blocking and non-blocking fibers’ behavior is identical.

Ruby doesn’t provide a scheduler class: it is expected to be implemented by the user and correspond to Fiber::Scheduler.

There is also Fiber.schedule method, which is expected to immediately perform the given block in a non-blocking manner. Its actual implementation is up to the scheduler.

Class methods 11

Instance methods 12

Ruby version changes 9

  1. 3.3
    • Introduce Fiber#kill. [Bug #595]

      Example
      fiber = Fiber.new do
        while true
          puts "Yielding..."
          Fiber.yield
        end
      ensure
        puts "Exiting..."
      end
      
      fiber.resume
      # Yielding...
      fiber.kill
      # Exiting...
      
  2. 3.2
    • Introduce Fiber.[] and Fiber.[]= for inheritable fiber storage. Introduce Fiber#storage and Fiber#storage= (experimental) for getting and resetting the current storage. Introduce Fiber.new(storage:) for setting the storage when creating a fiber. [Feature #19078]

      Existing Thread and Fiber local variables can be tricky to use. Thread-local variables are shared between all fibers, making it hard to isolate, while Fiber-local variables can be hard to share. It is often desirable to define unit of execution (“execution context”) such that some state is shared between all fibers and threads created in that context. This is what Fiber storage provides.

      Example
      def log(message)
        puts "#{Fiber[:request_id]}: #{message}"
      end
      
      def handle_requests
        while request = read_request
          Fiber.schedule do
            Fiber[:request_id] = SecureRandom.uuid
      
            request.messages.each do |message|
              Fiber.schedule do
                log("Handling #{message}") # Log includes inherited request_id.
              end
            end
          end
        end
      end
      

      You should generally consider Fiber storage for any state which you want to be shared implicitly between all fibers and threads created in a given context, e.g. a connection pool, a request id, a logger level, environment variables, configuration, etc.

  3. 3.0