Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added missing fiber sigs #1071

Merged
merged 6 commits into from
Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
318 changes: 304 additions & 14 deletions core/fiber.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,114 @@
# 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
Expand All @@ -85,7 +193,7 @@ class Fiber < Object
# 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
Expand All @@ -110,22 +218,106 @@ class Fiber < Object
# 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 level3
# Fiber.yield
# end
#
def resume: (*untyped args) -> untyped
# 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
Expand All @@ -147,7 +339,105 @@ class Fiber < Object
# 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
Loading