From b9a91334c50e5a0b8ea0a4b571cd3011228cba6c Mon Sep 17 00:00:00 2001 From: normal Date: Mon, 15 Jun 2015 19:38:49 +0000 Subject: socket: allow exception-free nonblocking sendmsg/recvmsg As documented before, exceptions are expensive and IO::Wait*able are too common in socket applications to be the exceptional case. Datagram sockets deserve the same API which stream sockets are allowed with read_nonblock and write_nonblock. Note: this does not offer a performance advantage under optimal conditions when both ends are equally matched in speed, but it it does make debug output cleaner by avoiding exceptions whenever the receiver slows down. * ext/socket/ancdata.c (bsock_sendmsg_internal, bsock_recvmsg_internal): support "exception: false" kwarg * ext/socket/init.c (rsock_s_recvfrom_nonblock): ditto * ext/socket/init.c (rsock_s_recvfrom_nonblock): use rsock_opt_false_p * ext/socket/socket.c (sock_connect_nonblock): ditto * ext/socket/rubysocket.h (rsock_opt_false_p): new function * ext/socket/basicsocket.c (bsock_recv_nonblock): update rdoc * ext/socket/udpsocket.c (udp_recvfrom_nonblock): ditto * test/socket/test_nonblock.rb: new tests [ruby-core:69542] [Feature #11229] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50910 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- test/socket/test_nonblock.rb | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'test/socket') diff --git a/test/socket/test_nonblock.rb b/test/socket/test_nonblock.rb index f0fba3af74..4e14c9355a 100644 --- a/test/socket/test_nonblock.rb +++ b/test/socket/test_nonblock.rb @@ -1,6 +1,7 @@ begin require "socket" require "io/nonblock" + require "io/wait" rescue LoadError end @@ -275,6 +276,17 @@ class TestSocketNonblock < Test::Unit::TestCase } end + def test_recvfrom_nonblock_no_exception + udp_pair do |s1, s2| + assert_equal :wait_readable, s1.recvfrom_nonblock(100, exception: false) + s2.send("aaa", 0, s1.getsockname) + assert s1.wait_readable + mesg, inet_addr = s1.recvfrom_nonblock(100, exception: false) + assert_equal(4, inet_addr.length) + assert_equal("aaa", mesg) + end + end + if defined?(UNIXSocket) && defined?(Socket::SOCK_SEQPACKET) def test_sendmsg_nonblock_seqpacket buf = '*' * 8192 @@ -286,6 +298,27 @@ class TestSocketNonblock < Test::Unit::TestCase rescue NotImplementedError, Errno::ENOSYS, Errno::EPROTONOSUPPORT skip "UNIXSocket.pair(:SEQPACKET) not implemented on this platform: #{$!}" end + + def test_sendmsg_nonblock_no_exception + buf = '*' * 128 + UNIXSocket.pair(:SEQPACKET) do |s1, s2| + n = 0 + Timeout.timeout(60) do + case rv = s1.sendmsg_nonblock(buf, exception: false) + when Integer + n += rv + when :wait_writable + break + else + flunk "unexpected return value: #{rv.inspect}" + end while true + assert_equal :wait_writable, rv + assert_operator n, :>, 0 + end + end + rescue NotImplementedError, Errno::ENOSYS, Errno::EPROTONOSUPPORT + skip "UNIXSocket.pair(:SEQPACKET) not implemented on this platform: #{$!}" + end end def test_recvmsg_nonblock_error @@ -297,6 +330,7 @@ class TestSocketNonblock < Test::Unit::TestCase rescue Errno::EWOULDBLOCK assert_kind_of(IO::WaitReadable, $!) end + assert_equal :wait_readable, s1.recvmsg_nonblock(11, exception: false) } end @@ -310,6 +344,16 @@ class TestSocketNonblock < Test::Unit::TestCase } end + def test_recv_nonblock_no_exception + tcp_pair {|c, s| + assert_equal :wait_readable, c.recv_nonblock(11, exception: false) + s.write('HI') + assert c.wait_readable + assert_equal 'HI', c.recv_nonblock(11, exception: false) + assert_equal :wait_readable, c.recv_nonblock(11, exception: false) + } + end + def test_connect_nonblock_error serv = TCPServer.new("127.0.0.1", 0) _, port, _, _ = serv.addr -- cgit v1.2.3