From bb6dfab2a8a798f68c7935596ec03855106d7fd4 Mon Sep 17 00:00:00 2001 From: normal Date: Mon, 16 Nov 2015 23:34:37 +0000 Subject: socket: Socket#connect_nonblock avoids arg parsing with C API * ext/socket/socket.c (sock_connect_nonblock): avoid argument parsing in C. [ruby-core:71439] [Feature #11339] * ext/socket/lib/socket.rb (Socket#connect_nonblock): new wrapper for private method, move RDoc target 0: a (ruby 2.3.0dev (2015-11-12 trunk 52540) [x86_64-linux]) target 1: b (ruby 2.3.0dev (2015-11-12 avoid-kwarg-capi 52540) [x86_64-linux] ----------------------------------------------------------- connect_nonblock require 'tempfile' require 'socket' require 'io/wait' nr = 500000 Tempfile.create(%w(connect_nonblock .sock)) do |tmp| path = tmp.path File.unlink(path) s = UNIXServer.new(path) addr = Socket.sockaddr_un(path).freeze nr.times do c = Socket.new(Socket::AF_UNIX, Socket::SOCK_STREAM) while c.connect_nonblock(addr, exception: false) == :wait_writable c.wait_writable end s.accept.close c.close end end ----------------------------------------------------------- raw data: [["connect_nonblock", [[4.014209181070328, 3.8479955345392227, 3.981342639774084, 4.471840236335993, 3.7867715656757355], [3.639054525643587, 3.58337214961648, 3.525284394621849, 3.52646067738533, 3.511393066495657]]]] Elapsed time: 37.889623996 (sec) ----------------------------------------------------------- benchmark results: minimum results in each 5 measurements. Execution time (sec) name a b connect_nonblock 3.787 3.511 Speedup ratio: compare with the result of `a' (greater is better) name b connect_nonblock 1.078 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52600 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/socket/lib/socket.rb | 47 ++++++++++++++++++++++++++++++++++ ext/socket/socket.c | 65 ++++++++---------------------------------------- 2 files changed, 57 insertions(+), 55 deletions(-) (limited to 'ext/socket') diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb index 6ee89e90d0..86da09e997 100644 --- a/ext/socket/lib/socket.rb +++ b/ext/socket/lib/socket.rb @@ -980,6 +980,53 @@ class Socket < BasicSocket } end + # call-seq: + # socket.connect_nonblock(remote_sockaddr, [options]) => 0 + # + # Requests a connection to be made on the given +remote_sockaddr+ after + # O_NONBLOCK is set for the underlying file descriptor. + # Returns 0 if successful, otherwise an exception is raised. + # + # === Parameter + # # +remote_sockaddr+ - the +struct+ sockaddr contained in a string or Addrinfo object + # + # === Example: + # # Pull down Google's web page + # require 'socket' + # include Socket::Constants + # socket = Socket.new(AF_INET, SOCK_STREAM, 0) + # sockaddr = Socket.sockaddr_in(80, 'www.google.com') + # begin # emulate blocking connect + # socket.connect_nonblock(sockaddr) + # rescue IO::WaitWritable + # IO.select(nil, [socket]) # wait 3-way handshake completion + # begin + # socket.connect_nonblock(sockaddr) # check connection failure + # rescue Errno::EISCONN + # end + # end + # socket.write("GET / HTTP/1.0\r\n\r\n") + # results = socket.read + # + # Refer to Socket#connect for the exceptions that may be thrown if the call + # to _connect_nonblock_ fails. + # + # Socket#connect_nonblock may raise any error corresponding to connect(2) failure, + # including Errno::EINPROGRESS. + # + # If the exception is Errno::EINPROGRESS, + # it is extended by IO::WaitWritable. + # So IO::WaitWritable can be used to rescue the exceptions for retrying connect_nonblock. + # + # By specifying `exception: false`, the options hash allows you to indicate + # that connect_nonblock should not raise an IO::WaitWritable exception, but + # return the symbol :wait_writable instead. + # + # === See + # # Socket#connect + def connect_nonblock(addr, exception: true) + __connect_nonblock(addr, exception) + end end class UDPSocket < IPSocket diff --git a/ext/socket/socket.c b/ext/socket/socket.c index 1a3320dbe5..a410625537 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -10,7 +10,7 @@ #include "rubysocket.h" -static VALUE sym_exception, sym_wait_writable; +static VALUE sym_wait_writable; static VALUE sock_s_unpack_sockaddr_in(VALUE, VALUE); @@ -439,62 +439,14 @@ sock_connect(VALUE sock, VALUE addr) return INT2FIX(n); } -/* - * call-seq: - * socket.connect_nonblock(remote_sockaddr, [options]) => 0 - * - * Requests a connection to be made on the given +remote_sockaddr+ after - * O_NONBLOCK is set for the underlying file descriptor. - * Returns 0 if successful, otherwise an exception is raised. - * - * === Parameter - * * +remote_sockaddr+ - the +struct+ sockaddr contained in a string or Addrinfo object - * - * === Example: - * # Pull down Google's web page - * require 'socket' - * include Socket::Constants - * socket = Socket.new(AF_INET, SOCK_STREAM, 0) - * sockaddr = Socket.sockaddr_in(80, 'www.google.com') - * begin # emulate blocking connect - * socket.connect_nonblock(sockaddr) - * rescue IO::WaitWritable - * IO.select(nil, [socket]) # wait 3-way handshake completion - * begin - * socket.connect_nonblock(sockaddr) # check connection failure - * rescue Errno::EISCONN - * end - * end - * socket.write("GET / HTTP/1.0\r\n\r\n") - * results = socket.read - * - * Refer to Socket#connect for the exceptions that may be thrown if the call - * to _connect_nonblock_ fails. - * - * Socket#connect_nonblock may raise any error corresponding to connect(2) failure, - * including Errno::EINPROGRESS. - * - * If the exception is Errno::EINPROGRESS, - * it is extended by IO::WaitWritable. - * So IO::WaitWritable can be used to rescue the exceptions for retrying connect_nonblock. - * - * By specifying `exception: false`, the options hash allows you to indicate - * that connect_nonblock should not raise an IO::WaitWritable exception, but - * return the symbol :wait_writable instead. - * - * === See - * * Socket#connect - */ +/* :nodoc: */ static VALUE -sock_connect_nonblock(int argc, VALUE *argv, VALUE sock) +sock_connect_nonblock(VALUE sock, VALUE addr, VALUE ex) { - VALUE addr; - VALUE opts = Qnil; VALUE rai; rb_io_t *fptr; int n; - rb_scan_args(argc, argv, "1:", &addr, &opts); SockAddrStringValueWithAddrinfo(addr, rai); addr = rb_str_new4(addr); GetOpenFile(sock, fptr); @@ -502,13 +454,13 @@ sock_connect_nonblock(int argc, VALUE *argv, VALUE sock) n = connect(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr)); if (n < 0) { if (errno == EINPROGRESS) { - if (rsock_opt_false_p(opts, sym_exception)) { + if (ex == Qfalse) { return sym_wait_writable; } rb_readwrite_sys_fail(RB_IO_WAIT_WRITABLE, "connect(2) would block"); } if (errno == EISCONN) { - if (rsock_opt_false_p(opts, sym_exception)) { + if (ex == Qfalse) { return INT2FIX(0); } } @@ -2113,7 +2065,11 @@ Init_socket(void) rb_define_method(rb_cSocket, "initialize", sock_initialize, -1); rb_define_method(rb_cSocket, "connect", sock_connect, 1); - rb_define_method(rb_cSocket, "connect_nonblock", sock_connect_nonblock, -1); + + /* for ext/socket/lib/socket.rb use only: */ + rb_define_private_method(rb_cSocket, + "__connect_nonblock", sock_connect_nonblock, 2); + rb_define_method(rb_cSocket, "bind", sock_bind, 1); rb_define_method(rb_cSocket, "listen", rsock_sock_listen, 1); rb_define_method(rb_cSocket, "accept", sock_accept, 0); @@ -2147,6 +2103,5 @@ Init_socket(void) rb_define_singleton_method(rb_cSocket, "ip_address_list", socket_s_ip_address_list, 0); #undef rb_intern - sym_exception = ID2SYM(rb_intern("exception")); sym_wait_writable = ID2SYM(rb_intern("wait_writable")); } -- cgit v1.2.3