Skip to content

Commit

Permalink
Add _Timeout type
Browse files Browse the repository at this point in the history
  • Loading branch information
sampersand committed Dec 19, 2023
1 parent 4eb84e2 commit 3bc2bce
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 12 deletions.
20 changes: 20 additions & 0 deletions core/builtin.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,26 @@ interface _Exception
| (String arg0) -> Exception
end

# A type that's used for durations.
#
# All numeric types implement this, but custom classes can also implement it if desired.
#
# Usage of `_Timeout` is fairly common throughout the stdlib, such as in `Kernel#sleep`,
# `IO#timeout=`, and `TCPSocket#new`'s `connet_timeout` field.
interface _Timeout
# Returns `[seconds, nanoseconds]`.
#
# The `seconds` should be a whole number of seconds, whereas the `nanoseconds` should be smaller
# than one. For example, `3.125.divmod(1)` would yield `[3, 0.125]`
def divmod: (1) -> [int, _TimeoutNSecs]
end

# The nanoseconds part of `_Timeout`'s return value. See it for details
interface _TimeoutNSecs
# Convert `self` into a whole number of seconds.
def *: (1_000_000_000) -> int
end

# Represents an `Integer`, or a type convertible to it (via `.to_int`).
#
type int = Integer | _ToInt
Expand Down
6 changes: 3 additions & 3 deletions core/io.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -2093,7 +2093,7 @@ class IO < Object
# -->
# Get the internal timeout duration or nil if it was not set.
#
def timeout: () -> Numeric?
def timeout: () -> _Timeout?

# <!--
# rdoc-file=io.c
Expand All @@ -2113,7 +2113,7 @@ class IO < Object
# effort to prevent an application from hanging on slow I/O operations, such as
# those that occur during a slowloris attack.
#
def timeout=: (Numeric? duration) -> void
def timeout=: (_Timeout? duration) -> void

# <!--
# rdoc-file=io.c
Expand Down Expand Up @@ -2989,7 +2989,7 @@ class IO < Object
# ping
#
def self.select: [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array) -> [ Array[X], Array[Y], Array[Z] ]
| [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array, Numeric? timeout) -> [ Array[X], Array[Y], Array[Z] ]?
| [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array, _Timeout? timeout) -> [ Array[X], Array[Y], Array[Z] ]?

# <!--
# rdoc-file=io.c
Expand Down
8 changes: 4 additions & 4 deletions core/io/wait.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class IO
#
# You must require 'io/wait' to use this method.
#
def wait: (Integer events, ?Numeric timeout) -> (Integer | false | nil)
| (?Numeric? timeout, *wait_mode mode) -> (self | true | false)
def wait: (Integer events, ?_Timeout timeout) -> (Integer | false | nil)
| (?_Timeout? timeout, *wait_mode mode) -> (self | true | false)

type wait_mode = :read | :r | :readable | :write | :w | :writable | :read_write | :rw | :readable_writable

Expand All @@ -54,7 +54,7 @@ class IO
#
# You must require 'io/wait' to use this method.
#
def wait_readable: (?Numeric? timeout) -> boolish
def wait_readable: (?_Timeout? timeout) -> boolish

# <!--
# rdoc-file=ext/io/wait/wait.c
Expand All @@ -66,5 +66,5 @@ class IO
#
# You must require 'io/wait' to use this method.
#
def wait_writable: (?Numeric? timeout) -> boolish
def wait_writable: (?_Timeout? timeout) -> boolish
end
5 changes: 3 additions & 2 deletions core/kernel.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -1547,7 +1547,7 @@ module Kernel : BasicObject
# (snipped)
# ping
#
def self?.select: (::Array[IO] read, ?::Array[IO] write, ?::Array[IO] error, ?Integer timeout) -> ::Array[String]
def self?.select: (::Array[IO] read, ?::Array[IO] write, ?::Array[IO] error, ?_Timeout timeout) -> ::Array[String]

# <!--
# rdoc-file=process.c
Expand All @@ -1566,8 +1566,9 @@ module Kernel : BasicObject
# Time.new #=> 2008-03-08 19:56:22 +0900
#
def self?.sleep: (?nil) -> bot
| (Integer | Float | _Divmod duration) -> Integer
| (_Timeout duration) -> Integer

%a{steep:deprecated}
interface _Divmod
def divmod: (Numeric) -> [ Numeric, Numeric ]
end
Expand Down
2 changes: 1 addition & 1 deletion core/thread.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -1437,7 +1437,7 @@ class Thread::ConditionVariable < Object
#
# Returns the slept result on `mutex`.
#
def wait: (Thread::Mutex mutex, ?Integer | Float? timeout) -> Integer?
def wait: (Thread::Mutex mutex, ?_Timeout? timeout) -> Integer?
end

# <!-- rdoc-file=thread_sync.c -->
Expand Down
4 changes: 2 additions & 2 deletions stdlib/socket/0/socket.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,8 @@ class Socket < BasicSocket
# puts sock.read
# }
#
def self.tcp: (String host, Integer port, ?String local_host, ?Integer local_port, ?resolv_timeout: Numeric, ?connect_timeout: Numeric) -> instance
| (String host, Integer port, ?String local_host, ?Integer local_port, ?resolv_timeout: Numeric, ?connect_timeout: Numeric) { (instance) -> void } -> void
def self.tcp: (String host, Integer port, ?String local_host, ?Integer local_port, ?resolv_timeout: _Timeout, ?connect_timeout: _Timeout) -> instance
| (String host, Integer port, ?String local_host, ?Integer local_port, ?resolv_timeout: _Timeout, ?connect_timeout: _Timeout) { (instance) -> void } -> void

# <!--
# rdoc-file=ext/socket/lib/socket.rb
Expand Down
23 changes: 23 additions & 0 deletions test/stdlib/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,29 @@ def with_boolish(&block)

alias with_untyped with_boolish

def with_duration(seconds: 1, nanoseconds: 0)
duration_obj = BlankSlate.new.__with_object_methods(:define_singleton_method)
nanosecs_obj = BlankSlate.new.__with_object_methods(:define_singleton_method)

secs = nanosecs = nil
duration_obj.define_singleton_method :divmod do |x|
flunk "Expected `1` for argument to divmod, got #{x.inspect}" unless 1.equal? x
[secs, nanosecs_obj]
end
nanosecs_obj.define_singleton_method :* do |x|
flunk "Expected `1000000000` for argument to *, got #{x.inspect}" unless 1000000000.equal? x
nanosecs
end

with_int seconds do |s|
secs = s
with_int nanoseconds do |ns|
nanosecs = ns
yield duration_obj
end
end
end

def with_range(start, stop, exclude_end = false)
# If you need fixed starting and stopping points, you can just do `with_range with(1), with(2)`.
raise ArgumentError, '`start` must be from a `with` method' unless start.is_a? WithEnum
Expand Down

0 comments on commit 3bc2bce

Please sign in to comment.