File: //usr/lib/ruby/gems/3.2.0/gems/rbs-2.8.2/core/fiber.rbs
# <!-- rdoc-file=cont.c -->
# 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
# <!--
# rdoc-file=cont.c
# - Fiber.blocking? -> false or 1
# -->
# Returns `false` if the current fiber is non-blocking. Fiber is non-blocking if
# it was created via passing `blocking: false` to Fiber.new, or via
# Fiber.schedule.
#
# If the current Fiber is blocking, the method returns 1. Future developments
# may allow for situations where larger integers could be returned.
#
# Note that, even if the method returns `false`, Fiber behaves differently only
# if Fiber.scheduler is set in the current thread.
#
# See the "Non-blocking fibers" section in class docs for details.
#
def self.blocking?: () -> untyped
# <!--
# rdoc-file=cont.c
# - Fiber.current -> fiber
# -->
# Returns the current fiber. If you are not running in the context of a fiber
# this method will return the root fiber.
#
def self.current: () -> Fiber
# <!--
# rdoc-file=cont.c
# - Fiber.current_scheduler -> obj or nil
# -->
# Returns the Fiber scheduler, that was last set for the current thread with
# Fiber.set_scheduler if and only if the current fiber is non-blocking.
#
def self.current_scheduler: () -> untyped?
# <!--
# rdoc-file=cont.c
# - Fiber.schedule { |*args| ... } -> fiber
# -->
# The method is *expected* to immediately run the provided block of code in a
# separate non-blocking fiber.
#
# puts "Go to sleep!"
#
# Fiber.set_scheduler(MyScheduler.new)
#
# Fiber.schedule do
# puts "Going to sleep"
# sleep(1)
# puts "I slept well"
# end
#
# puts "Wakey-wakey, sleepyhead"
#
# Assuming MyScheduler is properly implemented, this program will produce:
#
# Go to sleep!
# Going to sleep
# Wakey-wakey, sleepyhead
# ...1 sec pause here...
# I slept well
#
# ...e.g. on the first blocking operation inside the Fiber (`sleep(1)`), the
# control is yielded to the outside code (main fiber), and *at the end of that
# execution*, the scheduler takes care of properly resuming all the blocked
# fibers.
#
# Note that the behavior described above is how the method is *expected* to
# behave, actual behavior is up to the current scheduler's implementation of
# Fiber::SchedulerInterface#fiber method. Ruby doesn't enforce this method to
# behave in any particular way.
#
# If the scheduler is not set, the method raises `RuntimeError (No scheduler is
# available!)`.
#
def self.schedule: () { () -> void } -> Fiber
# <!--
# rdoc-file=cont.c
# - Fiber.scheduler -> obj or nil
# -->
# Returns the Fiber scheduler, that was last set for the current thread with Fiber.set_scheduler.
# Returns +nil+ if no scheduler is set (which is the default), and non-blocking fibers'
#
# # behavior is the same as blocking.
# (see "Non-blocking fibers" section in class docs for details about the scheduler concept).
#
def self.scheduler: () -> untyped?
# <!--
# rdoc-file=cont.c
# - Fiber.set_scheduler(scheduler) -> scheduler
# -->
# Sets the Fiber scheduler for the current thread. If the scheduler is set,
# non-blocking fibers (created by Fiber.new with `blocking: false`, or by
# Fiber.schedule) call that scheduler's hook methods on potentially blocking
# operations, and the current thread will call scheduler's `close` method on
# finalization (allowing the scheduler to properly manage all non-finished
# fibers).
#
# `scheduler` can be an object of any class corresponding to
# Fiber::SchedulerInterface. Its implementation is up to the user.
#
# See also the "Non-blocking fibers" section in class docs.
#
def self.set_scheduler: (untyped) -> untyped
# <!--
# rdoc-file=cont.c
# - Fiber.yield(args, ...) -> obj
# -->
# 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) -> untyped
# <!--
# rdoc-file=cont.c
# - Fiber.new(blocking: false) { |*args| ... } -> fiber
# -->
# 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: (?blocking: boolish) { (*untyped) -> void } -> void
# <!--
# rdoc-file=cont.c
# - fiber.alive? -> true or false
# -->
# Returns true if the fiber can still be resumed (or transferred to). After
# finishing execution of the fiber block this method will always return `false`.
#
def alive?: () -> bool
# <!--
# rdoc-file=cont.c
# - fiber.backtrace -> array
# - fiber.backtrace(start) -> array
# - fiber.backtrace(start, count) -> array
# - fiber.backtrace(start..end) -> array
# -->
# Returns the current execution stack of the fiber. `start`, `count` and `end`
# allow to select only parts of the backtrace.
#
# def level3
# Fiber.yield
# end
#
# def level2
# level3
# end
#
# def level1
# level2
# end
#
# f = Fiber.new { level1 }
#
# # It is empty before the fiber started
# f.backtrace
# #=> []
#
# f.resume
#
# f.backtrace
# #=> ["test.rb:2:in `yield'", "test.rb:2:in `level3'", "test.rb:6:in `level2'", "test.rb:10:in `level1'", "test.rb:13:in `block in <main>'"]
# p f.backtrace(1) # start from the item 1
# #=> ["test.rb:2:in `level3'", "test.rb:6:in `level2'", "test.rb:10:in `level1'", "test.rb:13:in `block in <main>'"]
# p f.backtrace(2, 2) # start from item 2, take 2
# #=> ["test.rb:6:in `level2'", "test.rb:10:in `level1'"]
# p f.backtrace(1..3) # take items from 1 to 3
# #=> ["test.rb:2:in `level3'", "test.rb:6:in `level2'", "test.rb:10:in `level1'"]
#
# f.resume
#
# # It is nil after the fiber is finished
# f.backtrace
# #=> nil
#
def backtrace: (?Integer start, ?Integer count) -> Array[String]?
| (Range[Integer]) -> Array[String]?
# <!--
# rdoc-file=cont.c
# - fiber.backtrace_locations -> array
# - fiber.backtrace_locations(start) -> array
# - fiber.backtrace_locations(start, count) -> array
# - fiber.backtrace_locations(start..end) -> array
# -->
# Like #backtrace, but returns each line of the execution stack as a
# Thread::Backtrace::Location. Accepts the same arguments as #backtrace.
#
# f = Fiber.new { Fiber.yield }
# f.resume
# loc = f.backtrace_locations.first
# loc.label #=> "yield"
# loc.path #=> "test.rb"
# loc.lineno #=> 1
#
def backtrace_locations: (?Integer start, ?Integer count) -> Array[Thread::Backtrace::Location]?
| (Range[Integer]) -> Array[Thread::Backtrace::Location]?
# <!--
# rdoc-file=cont.c
# - fiber.blocking? -> true or false
# -->
# Returns `true` if `fiber` is blocking and `false` otherwise. Fiber is
# non-blocking if it was created via passing `blocking: false` to Fiber.new, or
# via Fiber.schedule.
#
# Note that, even if the method returns `false`, the fiber behaves differently
# only if Fiber.scheduler is set in the current thread.
#
# See the "Non-blocking fibers" section in class docs for details.
#
def blocking?: () -> bool
# <!--
# rdoc-file=cont.c
# - inspect()
# -->
#
alias inspect to_s
# <!--
# rdoc-file=cont.c
# - fiber.raise -> obj
# - fiber.raise(string) -> obj
# - fiber.raise(exception [, string [, array]]) -> obj
# -->
# 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: (?string msg) -> untyped
| (_Exception, ?string msg, ?Array[string] backtrace) -> untyped
# <!--
# rdoc-file=cont.c
# - fiber.resume(args, ...) -> obj
# -->
# 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) -> untyped
# <!--
# rdoc-file=cont.c
# - to_s()
# -->
#
def to_s: () -> untyped
# <!--
# rdoc-file=cont.c
# - fiber.transfer(args, ...) -> obj
# -->
# Transfer control to another fiber, resuming it from where it last stopped or
# starting it if it was not resumed before. The calling fiber will be suspended
# much like in a call to Fiber.yield.
#
# The fiber which receives the transfer call treats it much like a resume call.
# Arguments passed to transfer are treated like those passed to resume.
#
# The two style of control passing to and from fiber (one is #resume and
# Fiber::yield, another is #transfer to and from fiber) can't be freely mixed.
#
# * If the Fiber's lifecycle had started with transfer, it will never be able
# to yield or be resumed control passing, only finish or transfer back. (It
# still can resume other fibers that are allowed to be resumed.)
# * If the Fiber's lifecycle had started with resume, it can yield or transfer
# to another Fiber, but can receive control back only the way compatible
# with the way it was given away: if it had transferred, it only can be
# transferred back, and if it had yielded, it only can be resumed back.
# After that, it again can transfer or yield.
#
#
# If those rules are broken FiberError is raised.
#
# For an individual Fiber design, yield/resume is easier to use (the Fiber just
# gives away control, it doesn't need to think about who the control is given
# to), while transfer is more flexible for complex cases, allowing to build
# arbitrary graphs of Fibers dependent on each other.
#
# Example:
#
# manager = nil # For local var to be visible inside worker block
#
# # This fiber would be started with transfer
# # It can't yield, and can't be resumed
# worker = Fiber.new { |work|
# puts "Worker: starts"
# puts "Worker: Performed #{work.inspect}, transferring back"
# # Fiber.yield # this would raise FiberError: attempt to yield on a not resumed fiber
# # manager.resume # this would raise FiberError: attempt to resume a resumed fiber (double resume)
# manager.transfer(work.capitalize)
# }
#
# # This fiber would be started with resume
# # It can yield or transfer, and can be transferred
# # back or resumed
# manager = Fiber.new {
# puts "Manager: starts"
# puts "Manager: transferring 'something' to worker"
# result = worker.transfer('something')
# puts "Manager: worker returned #{result.inspect}"
# # worker.resume # this would raise FiberError: attempt to resume a transferring fiber
# Fiber.yield # this is OK, the fiber transferred from and to, now it can yield
# puts "Manager: finished"
# }
#
# puts "Starting the manager"
# manager.resume
# puts "Resuming the manager"
# # manager.transfer # this would raise FiberError: attempt to transfer to a yielding fiber
# manager.resume
#
# *produces*
#
# Starting the manager
# Manager: starts
# Manager: transferring 'something' to worker
# Worker: starts
# Worker: Performed "something", transferring back
# Manager: worker returned "Something"
# Resuming the manager
# Manager: finished
#
def transfer: (*untyped) -> untyped
end