Skip to content

Expose RBS type signatures. #400

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions Steepfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
D = Steep::Diagnostic

target :lib do
signature "sig"
check "lib"
end
2 changes: 2 additions & 0 deletions gems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
gem "rubocop"
gem "rubocop-socketry"

gem "steep", git: "https://github.com/soutaro/steep"

gem "sus-fixtures-async"
gem "sus-fixtures-console"
gem "sus-fixtures-time"
Expand Down
12 changes: 7 additions & 5 deletions lib/async/barrier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module Async
# @public Since *Async v1*.
class Barrier
# Initialize the barrier.
# @parameter parent [Task | Semaphore | Nil] The parent for holding any children tasks.
# @parameter parent [_Asyncable?] The parent for holding any children tasks.
# @public Since *Async v1*.
def initialize(parent: nil)
@tasks = List.new
Expand All @@ -32,16 +32,17 @@ def initialize(task)

private_constant :TaskNode

# Number of tasks being held by the barrier.
# @returns [Integer] Number of tasks being held by the barrier.
def size
@tasks.size
end

# All tasks which have been invoked into the barrier.
# @attribute [Array(Task)] All tasks which have been invoked into the barrier.
attr :tasks

# Execute a child task and add it to the barrier.
# @asynchronous Executes the given block concurrently.
# @rbs [T] (*untyped, parent: _Asyncable, **untyped) {(Task, *untyped) -> T} -> Task[T]
def async(*arguments, parent: (@parent or Task.current), **options, &block)
waiting = nil

Expand All @@ -54,15 +55,15 @@ def async(*arguments, parent: (@parent or Task.current), **options, &block)
end
end

# Whether there are any tasks being held by the barrier.
# @returns [Boolean]
# @returns [Boolean] Whether there are any tasks being held by the barrier.
def empty?
@tasks.empty?
end

# Wait for all tasks to complete by invoking {Task#wait} on each waiting task, which may raise an error. As long as the task has completed, it will be removed from the barrier.
#
# @yields {|task| ...} If a block is given, the unwaited task is yielded. You must invoke {Task#wait} yourself. In addition, you may `break` if you have captured enough results.
# @parameter task [Task] The task which has completed.
#
# @asynchronous Will wait for tasks to finish executing.
def wait
Expand All @@ -88,6 +89,7 @@ def wait

# Stop all tasks held by the barrier.
# @asynchronous May wait for tasks to finish executing.
# @rbs () -> void
def stop
@tasks.each do |waiting|
waiting.task.stop
Expand Down
2 changes: 1 addition & 1 deletion lib/async/condition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def waiting?
end

# Signal to a given task that it should resume operations.
# @parameter value [Object | Nil] The value to return to the waiting fibers.
# @parameter value [::Object | Nil] The value to return to the waiting fibers.
def signal(value = nil)
return if @waiting.empty?

Expand Down
4 changes: 2 additions & 2 deletions lib/async/idler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Idler
#
# @parameter maximum_load [Numeric] The maximum load before we start shedding work.
# @parameter backoff [Numeric] The initial backoff time, used for delaying work.
# @parameter parent [Interface(:async) | Nil] The parent task to use for async operations.
# @parameter parent [_Asyncable | Nil] The parent task to use for async operations.
def initialize(maximum_load = 0.8, backoff: 0.01, parent: nil)
@maximum_load = maximum_load
@backoff = backoff
Expand All @@ -24,7 +24,7 @@ def initialize(maximum_load = 0.8, backoff: 0.01, parent: nil)
# @asynchronous Executes the given block concurrently.
#
# @parameter arguments [Array] The arguments to pass to the block.
# @parameter parent [Interface(:async) | Nil] The parent task to use for async operations.
# @parameter parent [_Asyncable] The parent task to use for async operations.
# @parameter options [Hash] The options to pass to the task.
# @yields {|task| ...} When the system is idle, the block will be executed in a new task.
def async(*arguments, parent: (@parent or Task.current), **options, &block)
Expand Down
1 change: 1 addition & 0 deletions lib/async/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def prepend(node)

# Add the node, yield, and the remove the node.
# @yields {|node| ...} Yields the node.
# @parameter node [Node] The node to add to the list.
# @returns [Object] Returns the result of the block.
def stack(node, &block)
append(node)
Expand Down
2 changes: 1 addition & 1 deletion lib/async/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def description

# Provides a backtrace for nodes that have an active execution context.
#
# @returns [Array(Thread::Backtrace::Locations) | Nil] The backtrace of the node, if available.
# @returns [Array(::Thread::Backtrace::Locations) | Nil] The backtrace of the node, if available.
def backtrace(*arguments)
nil
end
Expand Down
7 changes: 6 additions & 1 deletion lib/async/notification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Async
class Notification < Condition
# Signal to a given task that it should resume operations.
#
# @returns [Boolean] if a task was signalled.
# @returns [bool] If a task was signalled.
def signal(value = nil, task: Task.current)
return false if @waiting.empty?

Expand All @@ -21,14 +21,19 @@ def signal(value = nil, task: Task.current)
end

Signal = Struct.new(:waiting, :value) do
# @returns [bool] Returns true if the signal is still alive.
def alive?
true
end

# Transfer the value to all waiting fibers.
# @returns [self]
def transfer
waiting.each do |fiber|
fiber.transfer(value) if fiber.alive?
end

return self
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/async/queue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ClosedError < RuntimeError

# Create a new queue.
#
# @parameter parent [Interface(:async) | Nil] The parent task to use for async operations.
# @parameter parent [_Asyncable | Nil] The parent task to use for async operations.
# @parameter available [Notification] The notification to use for signaling when items are available.
def initialize(parent: nil, available: Notification.new)
@items = []
Expand Down Expand Up @@ -104,7 +104,7 @@ def pop
# @asynchronous Executes the given block concurrently for each item.
#
# @parameter arguments [Array] The arguments to pass to the block.
# @parameter parent [Interface(:async) | Nil] The parent task to use for async operations.
# @parameter parent [_Asyncable | Nil] The parent task to use for async operations.
# @parameter options [Hash] The options to pass to the task.
# @yields {|task| ...} When the system is idle, the block will be executed in a new task.
def async(parent: (@parent or Task.current), **options, &block)
Expand Down
4 changes: 2 additions & 2 deletions lib/async/scheduler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def blocking_operation_wait(work)
#
# @public Since *Async v1*.
# @parameter parent [Node | Nil] The parent node to use for task hierarchy.
# @parameter selector [IO::Event::Selector] The selector to use for event handling.
# @parameter selector [::IO::Event::Selector] The selector to use for event handling.
def initialize(parent = nil, selector: nil, profiler: Profiler&.default, worker_pool: WORKER_POOL)
super(parent)

Expand Down Expand Up @@ -197,7 +197,7 @@ def yield
end

# Schedule a fiber (or equivalent object) to be resumed on the next loop through the reactor.
# @parameter fiber [Fiber | Object] The object to be resumed on the next iteration of the run-loop.
# @parameter fiber [Any(Fiber, Object)] The object to be resumed on the next iteration of the run-loop.
def push(fiber)
@selector.push(fiber)
end
Expand Down
5 changes: 4 additions & 1 deletion lib/async/task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def initialize(message = "execution expired")
end

# @public Since *Async v1*.
# @rbs generic ResultType
class Task < Node
# Raised when a child task is created within a task that has finished execution.
class FinishedError < RuntimeError
Expand Down Expand Up @@ -224,6 +225,7 @@ def run(*arguments)
# @yields {|task| ...} in the context of the new task.
# @raises [FinishedError] If the task has already finished.
# @returns [Task] The child task.
# @rbs [T] (*untyped, **untyped) { (Task[T], *untyped, **untyped) -> T } -> Task[T]
def async(*arguments, **options, &block)
raise FinishedError if self.finished?

Expand All @@ -247,6 +249,7 @@ def async(*arguments, **options, &block)
#
# @raises [RuntimeError] If the task's fiber is the current fiber.
# @returns [Object] The final expression/result of the task's block.
# @rbs () -> ResultType
def wait
raise "Cannot wait on own fiber!" if Fiber.current.equal?(@fiber)

Expand Down Expand Up @@ -367,7 +370,7 @@ def self.current
end

# Check if there is a task defined for the current fiber.
# @returns [Interface(:async) | Nil]
# @returns [_Asyncable | Nil]
def self.current?
Fiber.current.async_task
end
Expand Down
18 changes: 10 additions & 8 deletions lib/async/variable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

module Async
# A synchronization primitive that allows one task to wait for another task to resolve a value.
# @rbs generic T
class Variable
# Create a new variable.
#
Expand All @@ -22,13 +23,14 @@ def initialize(condition = Condition.new)
#
# @parameter value [Object] The value to resolve.
def resolve(value = true)
@value = value
condition = @condition
@condition = nil

self.freeze

condition.signal(value)
if condition = @condition
@value = value
@condition = nil

self.freeze

condition.signal(value)
end
end

# Alias for {#resolve}.
Expand All @@ -45,7 +47,7 @@ def resolved?

# Wait for the value to be resolved.
#
# @returns [Object] The resolved value.
# @returns [T?] The resolved value.
def wait
@condition&.wait
return @value
Expand Down
6 changes: 3 additions & 3 deletions lib/async/waiter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ module Async
class Waiter
# Create a waiter instance.
#
# @parameter parent [Interface(:async) | Nil] The parent task to use for asynchronous operations.
# @parameter finished [Async::Condition] The condition to signal when a task completes.
# @parameter parent [_Asyncable | Nil] The parent task to use for asynchronous operations.
# @parameter finished [::Async::Condition] The condition to signal when a task completes.
def initialize(parent: nil, finished: Async::Condition.new)
warn("`Async::Waiter` is deprecated, use `Async::Barrier` instead.", uplevel: 1, category: :deprecated) if $VERBOSE

Expand All @@ -34,7 +34,7 @@ def async(parent: (@parent or Task.current), **options, &block)

# Wait for the first `count` tasks to complete.
# @parameter count [Integer | Nil] The number of tasks to wait for.
# @returns [Array(Async::Task)] If an integer is given, the tasks which have completed.
# @returns [Array(::Async::Task)] If an integer is given, the tasks which have completed.
# @returns [Async::Task] Otherwise, the first task to complete.
def first(count = nil)
minimum = count || 1
Expand Down
1 change: 1 addition & 0 deletions lib/kernel/async.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module Kernel
#
# @public Since *Async v1*.
# @asynchronous May block until given block completes executing.
# @rbs [ResultType] (*untyped, **untyped) { (Task) -> ResultType } -> Task[ResultType]
def Async(...)
if current = ::Async::Task.current?
return current.async(...)
Expand Down
1 change: 1 addition & 0 deletions lib/kernel/sync.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module Kernel
#
# @public Since *Async v1*.
# @asynchronous Will block until given block completes executing.
# @rbs [ResultType] (**untyped) { (Task) -> ResultType } -> ResultType
def Sync(annotation: nil, &block)
if task = ::Async::Task.current?
if annotation
Expand Down
Loading
Loading