* [PATCH] socket: avoid fcntl for read/write_nonblock on Linux
@ 2017-03-24 5:46 Eric Wong
0 siblings, 0 replies; only message in thread
From: Eric Wong @ 2017-03-24 5:46 UTC (permalink / raw)
To: spew
On platforms where MSG_DONTWAIT works reliably on all sockets
(so far, I know of Linux), we can avoid fcntl syscalls and
implement IO#write_nonblock and IO#read_nonblock in terms of the
socket-specific send and recv family of syscalls.
This avoids side effects on the socket, and also encourages
generic code to be written in cases where IO wrappers like
OpenSSL::SSL::SSLSocket are used.
---
ext/socket/lib/socket.rb | 18 ++++++++++++++++++
test/socket/test_basicsocket.rb | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 54 insertions(+)
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index ad7d1e7aa0..ff20453207 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -442,6 +442,24 @@ def recvmsg_nonblock(dlen = nil, flags = 0, clen = nil,
scm_rights: false, exception: true)
__recvmsg_nonblock(dlen, flags, clen, scm_rights, exception)
end
+
+ # Linux-specific optimizations to avoid fcntl for IO#read_nonblock
+ # and IO#write_nonblock using MSG_DONTWAIT
+ # Do other platforms suport MSG_DONTWAIT reliably?
+ if RUBY_PLATFORM =~ /linux/ && Socket.const_defined?(:MSG_DONTWAIT)
+ def read_nonblock(len, str = nil, exception: true) # :nodoc:
+ case rv = __recv_nonblock(len, 0, str, exception)
+ when '' # recv_nonblock returns empty string on EOF
+ exception ? raise(EOFError, 'end of file reached') : nil
+ else
+ rv
+ end
+ end
+
+ def write_nonblock(buf, exception: true) # :nodoc:
+ __sendmsg_nonblock(buf, 0, nil, nil, exception)
+ end
+ end
end
class Socket < BasicSocket
diff --git a/test/socket/test_basicsocket.rb b/test/socket/test_basicsocket.rb
index e17a675d8a..0bd0408d83 100644
--- a/test/socket/test_basicsocket.rb
+++ b/test/socket/test_basicsocket.rb
@@ -152,4 +152,40 @@ def test_for_fd
sock.close
end
end
+
+ def test_read_write_nonblock
+ socks do |sserv, ssock, csock|
+ buf = String.new
+ assert_equal :wait_readable, ssock.read_nonblock(1, buf, exception: false)
+ assert_equal 5, csock.write_nonblock('hello')
+ IO.select([ssock])
+ assert_same buf, ssock.read_nonblock(5, buf, exception: false)
+ assert_equal 'hello', buf
+ buf = '*' * 16384
+ n = 0
+
+ case w = csock.write_nonblock(buf, exception: false)
+ when Integer
+ n += w
+ when :wait_writable
+ break
+ end while true
+
+ assert_equal :wait_writable, w
+ assert_raise(IO::WaitWritable) { loop { csock.write_nonblock(buf) } }
+ assert_operator n, :>, 0
+ csock.close
+
+ case r = ssock.read_nonblock(16384, buf, exception: false)
+ when String
+ next
+ when nil
+ break
+ else
+ flunk "unexpected read_nonblock return: #{r.inspect}"
+ end while true
+
+ assert_raise(EOFError) { ssock.read_nonblock(1) }
+ end
+ end
end if defined?(BasicSocket)
--
EW
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2017-03-24 5:46 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-24 5:46 [PATCH] socket: avoid fcntl for read/write_nonblock on Linux Eric Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).