Skip to content

Commit

Permalink
Adding IO#timeout.
Browse files Browse the repository at this point in the history
  • Loading branch information
itarato committed May 25, 2023
1 parent ab9f1a6 commit f9612a2
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Compatibility:
* Fix `Range#size` and return `nil` for beginningless Range when end isn't Numeric (#3039, @rwstauner).
* Alias `String#-@` to `String#dedup` (#3039, @itarato).
* Fix `Pathname#relative_path_from` to convert string arguments to Pathname objects (@rwstauner).
* Adding `IO#timeout` and `IO#timeout=` (#3039, @itarato).

Performance:

Expand Down
14 changes: 14 additions & 0 deletions src/main/ruby/truffleruby/core/io.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class IO

include Enumerable

class TimeoutError < IOError; end

module WaitReadable; end
module WaitWritable; end

Expand Down Expand Up @@ -1648,6 +1650,18 @@ def printf(fmt, *args)
write sprintf(fmt, *args)
end

def timeout
@timeout || nil
end

def timeout=(new_timeout)
if Primitive.nil?(timeout) ^ Primitive.nil?(new_timeout)
self.nonblock = !Primitive.nil?(new_timeout)
end

@timeout = new_timeout
end

def read(length = nil, buffer = nil)
ensure_open_and_readable
buffer = StringValue(buffer) if buffer
Expand Down
64 changes: 57 additions & 7 deletions src/main/ruby/truffleruby/core/posix.rb
Original file line number Diff line number Diff line change
Expand Up @@ -401,10 +401,10 @@ def self.read_string_nonblock(io, count, exception)
# by IO#sysread

def self.read_string_native(io, length)
fd = io.fileno
buffer = Primitive.io_thread_buffer_allocate(length)
begin
bytes_read = Truffle::POSIX.read(fd, buffer, length)
bytes_read = execute_posix_read(io, buffer, length)

if bytes_read < 0
bytes_read, errno = bytes_read, Errno.errno
elsif bytes_read == 0 # EOF
Expand All @@ -425,11 +425,62 @@ def self.read_string_native(io, length)
end
end

def self.read_to_buffer_native(io, length)
def self.execute_posix_read(io, buffer, length)
fd = io.fileno
return Truffle::POSIX.read(fd, buffer, length) unless io.timeout

deadline = Time.now.to_f + io.timeout

loop do
current_timeout = deadline - Time.now.to_f
raise IO::TimeoutError if current_timeout < 0

poll_result = Truffle::IOOperations.poll(io, Truffle::IOOperations::POLLIN, current_timeout)
if poll_result == 0
raise IO::TimeoutError
elsif poll_result == -1
Errno.handle_errno(Errno.errno)
end

if (bytes_read = Truffle::POSIX.read(fd, buffer, length)) == -1
continue if Errno.errno == Errno::EAGAIN
break
end

return bytes_read
end
end

def self.execute_posix_write(io, buffer, length)
fd = io.fileno
return Truffle::POSIX.write(fd, buffer, length) unless io.timeout

deadline = Time.now.to_f + io.timeout

loop do
current_timeout = deadline - Time.now.to_f
raise IO::TimeoutError if current_timeout < 0

poll_result = Truffle::IOOperations.poll(io, Truffle::IOOperations::POLLOUT, current_timeout)
if poll_result == 0
raise IO::TimeoutError
elsif poll_result == -1
Errno.handle_errno(Errno.errno)
end

if (bytes_written = Truffle::POSIX.write(fd, buffer, length)) == -1
continue if Errno.errno == Errno::EAGAIN
break
end

return bytes_written
end
end

def self.read_to_buffer_native(io, length)
buffer = Primitive.io_thread_buffer_allocate(length)
begin
bytes_read = Truffle::POSIX.read(fd, buffer, length)
bytes_read = execute_posix_read(io, buffer, length)
if bytes_read < 0
bytes_read, errno = bytes_read, Errno.errno
elsif bytes_read == 0 # EOF
Expand Down Expand Up @@ -495,7 +546,7 @@ def self.write_string_native(io, string, continue_on_eagain)

written = 0
while written < length
ret = Truffle::POSIX.write(fd, buffer + written, length - written)
ret = execute_posix_write(io, buffer + written, length - written)
if ret < 0
errno = Errno.errno
if errno == EAGAIN_ERRNO
Expand Down Expand Up @@ -540,12 +591,11 @@ def self.write_string_polyglot(io, string, continue_on_eagain)
# #write_string_nonblock_polylgot) is called by IO#write_nonblock

def self.write_string_nonblock_native(io, string)
fd = io.fileno
length = string.bytesize
buffer = Primitive.io_thread_buffer_allocate(length)
begin
buffer.write_bytes string
written = Truffle::POSIX.write(fd, buffer, length)
written = execute_posix_write(io, buffer, length)

if written < 0
errno = Errno.errno
Expand Down

0 comments on commit f9612a2

Please sign in to comment.