# # 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: # # 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: # # 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::SchedulerInterface. # # 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 Fiber < Object # # Yields control back to the context that resumed the fiber, passing along any # arguments that were passed to it. The fiber will resume processing at this # point when #resume is called next. Any arguments passed to the next #resume # will be the value that this Fiber.yield expression evaluates to. # def self.yield: (*untyped args) -> untyped # # Creates new Fiber. Initially, the fiber is not running and can be resumed with # #resume. Arguments to the first #resume call will be passed to the block: # # f = Fiber.new do |initial| # current = initial # loop do # puts "current: #{current.inspect}" # current = Fiber.yield # end # end # f.resume(100) # prints: current: 100 # f.resume(1, 2, 3) # prints: current: [1, 2, 3] # f.resume # prints: current: nil # # ... and so on ... # # If `blocking: false` is passed to `Fiber.new`, *and* current thread has a # Fiber.scheduler defined, the Fiber becomes non-blocking (see "Non-blocking # Fibers" section in class docs). # def initialize: () { () -> untyped } -> void # # Resumes the fiber from the point at which the last Fiber.yield was called, or # starts running it if it is the first call to #resume. Arguments passed to # resume will be the value of the Fiber.yield expression or will be passed as # block parameters to the fiber's block if this is the first #resume. # # Alternatively, when resume is called it evaluates to the arguments passed to # the next Fiber.yield statement inside the fiber's block or to the block value # if it runs to completion without any Fiber.yield # def resume: (*untyped args) -> untyped # # Raises an exception in the fiber at the point at which the last `Fiber.yield` # was called. If the fiber has not been started or has already run to # completion, raises `FiberError`. If the fiber is yielding, it is resumed. If # it is transferring, it is transferred into. But if it is resuming, raises # `FiberError`. # # With no arguments, raises a `RuntimeError`. With a single `String` argument, # raises a `RuntimeError` with the string as a message. Otherwise, the first # parameter should be the name of an `Exception` class (or an object that # returns an `Exception` object when sent an `exception` message). The optional # second parameter sets the message associated with the exception, and the third # parameter is an array of callback information. Exceptions are caught by the # `rescue` clause of `begin...end` blocks. # def raise: () -> untyped | (string message) -> untyped | (_Exception exception, ?string message, ?Array[String] backtrace) -> untyped end