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: cabf83c93bff
Choose a base ref
...
head repository: rubinius/rubinius
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: b62dcfa15f8f
Choose a head ref
  • 2 commits
  • 2 files changed
  • 1 contributor

Commits on Oct 6, 2015

  1. Copy the full SHA
    341c139 View commit details
  2. when a write returns EAGAIN or EINTR test to see if in non-blocking mode

    Also took the opportunity to refactor fcntl F_GETFL and F_SETFL
    chuckremes committed Oct 6, 2015
    Copy the full SHA
    b62dcfa View commit details
Showing with 72 additions and 30 deletions.
  1. +63 −30 kernel/common/io.rb
  2. +9 −0 spec/ruby/core/io/shared/write.rb
93 changes: 63 additions & 30 deletions kernel/common/io.rb
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ def self.new_open_fd(new_fd)
if new_fd > 2
flags = FFI::Platform::POSIX.fcntl(new_fd, F_GETFD, 0)
Errno.handle("fcntl(2) failed") if FFI.call_failed?(flags)
flags = FFI::Platform::POSIX.fcntl(new_fd, F_SETFD, FFI::Platform::POSIX.fcntl(new_fd, F_GETFL, 0) | FD_CLOEXEC)
flags = FFI::Platform::POSIX.fcntl(new_fd, F_SETFD, get_flags(new_fd) | FD_CLOEXEC)
Errno.handle("fcntl(2) failed") if FFI.call_failed?(flags)
end

@@ -122,10 +122,41 @@ def self.update_max_fd(new_fd)
@@max_descriptors.get_and_set(new_fd)
end

def self.get_flags(fd)
if IO::F_GETFL
if FFI.call_failed?(flags = FFI::Platform::POSIX.fcntl(fd, IO::F_GETFL, 0))
Errno.handle("fcntl(2) failed")
end
else
flags = 0
end
flags
end

def self.clear_flag(flag, fd)
flags = get_flags(fd)
if (flags & flag) == 0
flags &= ~flag
if FFI.call_failed?(flags = FFI::Platform::POSIX.fcntl(fd, IO::F_SETFL, flags))
Errno.handle("fcntl(2) failed")
end
end
end

def self.set_flag(flag, fd)
flags = get_flags(fd)
if (flags & flag) == 0
flags |= flag
if FFI.call_failed?(flags = FFI::Platform::POSIX.fcntl(fd, IO::F_SETFL, flags))
Errno.handle("fcntl(2) failed")
end
end
end


def initialize(fd, stat)
@descriptor, @stat = fd, stat
acc_mode = FFI::Platform::POSIX.fcntl(@descriptor, F_GETFL, 0)
acc_mode = FileDescriptor.get_flags(@descriptor)

if acc_mode < 0
# Assume it's closed.
@@ -264,7 +295,12 @@ def write(str)
errno = Errno.errno
if errno == Errno::EINTR::Errno || errno == Errno::EAGAIN::Errno
# do a #select and wait for descriptor to become writable
continue
if blocking?
Select.wait_for_writable(@descriptor)
next
else
break
end
elsif errno == Errno::EPIPE::Errno
if @descriptor == 1 || @descriptor == 2
return(buf_size)
@@ -438,32 +474,21 @@ def seek_positioning
private :seek_positioning

def set_mode
if IO::F_GETFL
if FFI.call_failed?(acc_mode = FFI::Platform::POSIX.fcntl(@descriptor, IO::F_GETFL, 0))
Errno.handle("failed")
end
else
acc_mode = 0
end
@mode = FileDescriptor.get_flags(@descriptor)
end

def blocking?
(FileDescriptor.get_flags(@descriptor) & O_NONBLOCK) == 0
end

@mode = acc_mode
def set_blocking
flags = FileDescriptor.get_flags(@descriptor)
FileDescriptor.clear_flag(O_NONBLOCK, @descriptor)
end

def set_nonblock
if IO::F_GETFL
if FFI.call_failed?(flags = FFI::Platform::POSIX.fcntl(@descriptor, IO::F_GETFL, 0))
Errno.handle("fcntl(2) failed")
end
else
flags = 0
end

if (flags & O_NONBLOCK) == 0
flags |= O_NONBLOCK
if FFI.call_failed?(flags = FFI::Platform::POSIX.fcntl(@descriptor, IO::F_SETFL, flags))
Errno.handle("fcntl(2) failed")
end
end
flags = FileDescriptor.get_flags(@descriptor)
FileDescriptor.set_flag(O_NONBLOCK, @descriptor)
end

def ftruncate(offset)
@@ -612,12 +637,12 @@ def write_nonblock(str)
buf_size = str.bytesize
left = buf_size

buffer = FFI::MemoryPointer.new(left)
buffer.write_string(str)
error = false

if left > 0
buffer = FFI::MemoryPointer.new(left)
buffer.write_string(str)

if FFI.call_failed?(bytes_written = FFI::Platform::POSIX.write(@descriptor, buffer, left))
set_block
Errno.handle("write_nonblock")
end

@@ -823,6 +848,14 @@ def self.readable_events(read_fd)
FFI::Platform::POSIX.select(read_fd + 1, fd_set.to_set, nil, nil, timer)
end

def self.wait_for_writable(fd)
fd_set = FDSet.new
fd_set.zero
fd_set.set(fd)

FFI::Platform::POSIX.select(fd + 1, nil, fd_set.to_set, nil, nil)
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)
@@ -1538,7 +1571,7 @@ def self.sysopen(path, mode = nil, perm = nil)
# The +sync+ attribute will also be set.
#
def self.setup(io, fd, mode=nil, sync=false)
cur_mode = FFI::Platform::POSIX.fcntl(fd, F_GETFL, 0)
cur_mode = FileDescriptor.get_flags(fd)
Errno.handle if cur_mode < 0

cur_mode &= ACCMODE
9 changes: 9 additions & 0 deletions spec/ruby/core/io/shared/write.rb
Original file line number Diff line number Diff line change
@@ -69,4 +69,13 @@
lambda { IOSpecs.closed_io.send(@method, "hello") }.should raise_error(IOError)
end

it "does not block when descriptor is set to nonblocking mode" do
r, w = IO.pipe
flags = Rubinius::FFI::Platform::POSIX.fcntl(w.fileno, IO::F_GETFL, 0)
Rubinius::FFI::Platform::POSIX.fcntl(w.fileno, IO::F_SETFL, flags | IO::O_NONBLOCK)

written = w.send(@method, 'a' * 1_000_000) # pick a number that will exceed buffer
written.should > 0
end

end