core/fiber.rbs in rbs-2.6.0 vs core/fiber.rbs in rbs-2.7.0.pre.1
- old
+ new
@@ -76,18 +76,126 @@
# 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 args) -> untyped
+ def self.yield: (*untyped) -> untyped
# <!--
# rdoc-file=cont.c
# - Fiber.new(blocking: false) { |*args| ... } -> fiber
# -->
@@ -108,29 +216,113 @@
#
# 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
+ def initialize: (?blocking: boolish) { (*untyped) -> void } -> void
# <!--
# rdoc-file=cont.c
- # - fiber.resume(args, ...) -> obj
+ # - fiber.alive? -> true or false
# -->
- # 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.
+ # 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`.
#
- # 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 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 resume: (*untyped args) -> untyped
+ # 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`
@@ -145,9 +337,107 @@
# 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
+ 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