Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: rubinius/rubinius
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2b90e737171f
Choose a base ref
...
head repository: rubinius/rubinius
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1d03e20a2223
Choose a head ref
  • 4 commits
  • 4 files changed
  • 1 contributor

Commits on Oct 2, 2015

  1. Copy the full SHA
    d17f9e0 View commit details
  2. Copy the full SHA
    4ce1738 View commit details
  3. Copy the full SHA
    99735d7 View commit details
  4. Copy the full SHA
    1d03e20 View commit details
Showing with 113 additions and 15 deletions.
  1. +4 −4 kernel/bootstrap/io.rb
  2. +13 −0 kernel/common/errno.rb
  3. +90 −11 kernel/common/io.rb
  4. +6 −0 spec/ruby/core/io/read_nonblock_spec.rb
8 changes: 4 additions & 4 deletions kernel/bootstrap/io.rb
Original file line number Diff line number Diff line change
@@ -65,10 +65,10 @@ def write2(str)
raise PrimitiveFailure, "IO#write primitive failed"
end

def read_if_available(size)
Rubinius.primitive :io_read_if_available
raise PrimitiveFailure, "IO#read_if_available primitive failed"
end
# def read_if_available(size)
# Rubinius.primitive :io_read_if_available
# raise PrimitiveFailure, "IO#read_if_available primitive failed"
# end

def raw_write(str)
Rubinius.primitive :io_write_nonblock
13 changes: 13 additions & 0 deletions kernel/common/errno.rb
Original file line number Diff line number Diff line change
@@ -24,4 +24,17 @@ def self.errno
def self.eql?(code)
FFI.errno == code
end

def self.raise_waitreadable(message=nil)
raise IO::EAGAINWaitReadable, message
end

def self.raise_eagain(message=nil)
raise_errno(message, Errno::EAGAIN::Errno)
end

def self.raise_errno(message, errno)
raise SystemCallError.new(message, errno)
end
private :raise_errno
end
101 changes: 90 additions & 11 deletions kernel/common/io.rb
Original file line number Diff line number Diff line change
@@ -526,6 +526,29 @@ def read(length, output_string=nil)
determine_eof
return output_string
end

def read_only_buffer(length)
unless @unget_buffer.empty?
if length >= @unget_buffer.size
@offset += @unget_buffer.size
length -= @unget_buffer.size

str = @unget_buffer.inject("".force_encoding(Encoding::ASCII_8BIT)) { |sum, val| val.chr + sum }
buffer_reset
[str, length]
else
@offset += @unget_buffer.size
str = "".force_encoding(Encoding::ASCII_8BIT)

length.times do
str << @unget_buffer.pop
end
[str, 0]
end
else
[nil, length]
end
end

def seek(bytes, whence)
# @offset may not match actual file pointer if there were calls to #unget.
@@ -741,6 +764,21 @@ def self.reset_timeval_timeout(time_limit, future)
timer_sub(future, now, time_limit)
end

def self.readable_events(read_fd)
fd_set = FDSet.new
fd_set.zero
fd_set.set(read_fd)

unless const_defined?(:Timeval_t)
# This is a complete hack.
Select.class_eval(Rubinius::Config['rbx.platform.timeval.class'])
end

timer = Timeval_t.new # sets fields to zero by default

FFI::Platform::POSIX.select(read_fd + 1, fd_set.to_set, nil, nil, timer)
end

def self.select(readables, writables, errorables, timeout)
read_set, highest_read_fd = readables.nil? ? [nil, nil] : fd_set_from_array(readables)
write_set, highest_write_fd = writables.nil? ? [nil, nil] : fd_set_from_array(writables)
@@ -749,7 +787,7 @@ def self.select(readables, writables, errorables, timeout)

unless const_defined?(:Timeval_t)
# This is a complete hack.
IO.class_eval(Rubinius::Config['rbx.platform.timeval.class'])
Select.class_eval(Rubinius::Config['rbx.platform.timeval.class'])
end

time_limit, future = make_timeval_timeout(timeout)
@@ -2501,6 +2539,36 @@ def read_all

private :read_all

def read_if_available(bytes)
return "" if bytes.zero?
buffer, bytes = @fd.read_only_buffer(bytes)

events = IO::Select.readable_events(descriptor)
if events == 0 && !buffer
Errno.raise_waitreadable("no data ready")
return ""
elsif events < 0 && !buffer
Errno.handle("read(2) failed")
return ""
elsif events == 0 && buffer
# we were able to read from the buffer but no more data is waiting
return buffer
end

# if we get here then we have data to read from the descriptor
str = ""
bytes_read = @fd.read(bytes, str)

if bytes_read.nil?
# there's a chance the read could fail even when we have data read from the buffer
# to return to caller
return (nil || buffer)
else
# combine what we read from the buffer with what we read from the descriptor
buffer = buffer.to_s + str
return buffer
end
end
# defined in bootstrap, used here.
private :read_if_available

@@ -2524,7 +2592,6 @@ def read_nonblock(size, buffer=nil)

buffer = StringValue buffer if buffer

##
if str = read_if_available(size)
buffer.replace(str) if buffer
return str
@@ -2636,23 +2703,35 @@ def readpartial(size, buffer=nil)

return buffer if size == 0

# if @ibuffer.size > 0
# data = @ibuffer.shift(size)
# else
data = sysread(size)
# end
data = nil
begin
data = read_nonblock(size)
rescue IO::WaitReadable
IO.select([self])
retry
rescue IO::WaitWritable
IO.select(nil, [self])
retry
end

buffer.replace(data)

return buffer
else
return "" if size == 0

# if #@ibuffer.size > 0
# return ##@ibuffer.shift(size)
# end
data = nil
begin
data = read_nonblock(size)
rescue IO::WaitReadable
IO.select([self])
retry
rescue IO::WaitWritable
IO.select(nil, [self])
retry
end

return sysread(size)
return data
end
end

6 changes: 6 additions & 0 deletions spec/ruby/core/io/read_nonblock_spec.rb
Original file line number Diff line number Diff line change
@@ -39,6 +39,12 @@
@read.read_nonblock(1).should == "1"
end

it "returns any buffered data in addition to reading new data" do
@write << "hello"
@read.ungetc("a")
@read.read_nonblock(10).should == "ahello"
end

not_compliant_on :rubinius, :jruby do
# TODO: Fix this.
#