Skip to content

Commit

Permalink
adding wait in
Browse files Browse the repository at this point in the history
  • Loading branch information
sampersand committed Dec 26, 2023
1 parent a337230 commit 3284d3b
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 51 deletions.
52 changes: 48 additions & 4 deletions core/io.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -3401,9 +3401,53 @@ class IO
def write_nonblock: (_ToS string, ?exception: true) -> Integer
| (_ToS string, exception: false) -> (Integer | :wait_writable)

def wait: (int events, Time::_Timeout? timeout) -> Integer?
| (?Time::_Timeout timeout, *wait_mode events) -> (IO | bool)?
| (*wait_mode | Time::_Timeout events_or_timeout) -> (IO | bool)?

type wait_mode = :read | :r | :readable | :write | :w | :writable | :read_write | :rw | :readable_writable
# <!--
# rdoc-file=ext/io/wait/wait.c
# - io.wait(events, timeout) -> event mask, false or nil
# - io.wait(timeout = nil, mode = :read) -> self, true, or false
# -->
# Waits until the IO becomes ready for the specified events and returns the
# subset of events that become ready, or a falsy value when times out.
#
# The events can be a bit mask of `IO::READABLE`, `IO::WRITABLE` or
# `IO::PRIORITY`.
#
# Returns a truthy value immediately when buffered data is available.
#
# Optional parameter `mode` is one of `:read`, `:write`, or `:read_write`.
#
%a{ruby:since:3.2.0}
def wait: (int events, Time::_Timeout timeout) -> Integer
| (int events, Time::_Timeout? timeout) -> Integer?
| (*wait_mode | Time::_Timeout events_or_timeout) -> (self | bool)?

# The mode to use in `IO#wait`.
type wait_mode = :r | :read | :readable | :w | :write | :writable | :rw | :read_write | :readable_writable

# <!--
# rdoc-file=ext/io/wait/wait.c
# - io.wait_readable -> truthy or falsy
# - io.wait_readable(timeout) -> truthy or falsy
# -->
# Waits until IO is readable and returns a truthy value, or a falsy value when
# times out. Returns a truthy value immediately when buffered data is
# available.
#
%a{ruby:since:3.2.0}
def wait_readable: (?Time::_Timeout? timeout) -> (self | bool)?

# <!--
# rdoc-file=ext/io/wait/wait.c
# - io.wait_writable -> truthy or falsy
# - io.wait_writable(timeout) -> truthy or falsy
# -->
# Waits until IO is writable and returns a truthy value or a falsy value when
# times out.
#
%a{ruby:since:3.2.0}
def wait_writable: (?Time::_Timeout? timeout) -> (self | bool)?

%a{ruby:since:3.2.0}
def wait_priority: (?Time::_Timeout? timeout) -> (self | bool)?
end
47 changes: 0 additions & 47 deletions core/io/wait.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -20,51 +20,4 @@ class IO
# You must require 'io/wait' to use this method.
#
def ready?: () -> boolish

# <!--
# rdoc-file=ext/io/wait/wait.c
# - io.wait(events, timeout) -> event mask, false or nil
# - io.wait(timeout = nil, mode = :read) -> self, true, or false
# -->
# Waits until the IO becomes ready for the specified events and returns the
# subset of events that become ready, or a falsy value when times out.
#
# The events can be a bit mask of `IO::READABLE`, `IO::WRITABLE` or
# `IO::PRIORITY`.
#
# Returns a truthy value immediately when buffered data is available.
#
# Optional parameter `mode` is one of `:read`, `:write`, or `:read_write`.
#
# 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)

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

# <!--
# rdoc-file=ext/io/wait/wait.c
# - io.wait_readable -> truthy or falsy
# - io.wait_readable(timeout) -> truthy or falsy
# -->
# Waits until IO is readable and returns a truthy value, or a falsy value when
# times out. Returns a truthy value immediately when buffered data is
# available.
#
# You must require 'io/wait' to use this method.
#
def wait_readable: (?Numeric? timeout) -> boolish

# <!--
# rdoc-file=ext/io/wait/wait.c
# - io.wait_writable -> truthy or falsy
# - io.wait_writable(timeout) -> truthy or falsy
# -->
# Waits until IO is writable and returns a truthy value or a falsy value when
# times out.
#
# You must require 'io/wait' to use this method.
#
def wait_writable: (?Numeric? timeout) -> boolish
end
20 changes: 20 additions & 0 deletions core/time.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,26 @@
# You can define this method per subclasses, or on the toplevel Time class.
#
class Time < Object
# A type that's used for timeouts.
#
# All numeric types implement this, but custom classes can also implement it if desired.
#
# Usage of `Time::_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 `Time::_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

include Comparable

# <!--
Expand Down
42 changes: 42 additions & 0 deletions test/stdlib/IO_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1308,4 +1308,46 @@ def test_write_nonblock
end
end
end

def test_wait
IO.pipe do |read, write|
with_int IO::READABLE do |events|
with_timeout(seconds: 0, nanoseconds: 500).and_nil do |timeout|
write.puts "Hello"
assert_send_type '(int, Time::_Timeout?) -> Integer',
read, :wait, events, timeout
read.gets

next if nil.equal?(timeout) # `nil` timeout will never return
assert_send_type '(int, Time::_Timeout) -> nil',
read, :wait, events, timeout
end
end

# For some reason, `IO.pipe`'s `read` can always be written to, so we need to make sure
# for the timeout part of the test, we use readable only.
%i[r read readable].each do |event|
with_timeout(seconds: 0, nanoseconds: 500) do |timeout|
assert_send_type '(*IO::wait_mode | Time::_Timeout) -> nil',
io, :wait, event, timeout, event
end
end

%i[r read readable w write writable rw read_write readable_writable].each do |event|
with_timeout(seconds: 0, nanoseconds: 500) do |timeout|
write.puts "hello"
assert_send_type '(*IO::wait_mode | Time::_Timeout) -> IO',
io, :wait, event, timeout, event
read.gets
end
end

# NB (@sampersand): I can't figure out how to get `IO#wait` to return `false`, but I can get
# it to return `true`. Since I can't prove to myself it'll never return `false`, I've left
# the signature as `bool`, not `true`.
read.ungetc '&'
assert_send_type '(*IO::wait_mode | Time::_Timeout) -> true',
io, :wait, :read, 0, :read
end
end
end

0 comments on commit 3284d3b

Please sign in to comment.