ruby-changes:40519
From: normal <ko1@a...>
Date: Tue, 17 Nov 2015 08:34:44 +0900 (JST)
Subject: [ruby-changes:40519] normal:r52600 (trunk): socket: Socket#connect_nonblock avoids arg parsing with C API
normal 2015-11-17 08:34:37 +0900 (Tue, 17 Nov 2015) New Revision: 52600 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=52600 Log: 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 Modified files: trunk/ChangeLog trunk/ext/socket/lib/socket.rb trunk/ext/socket/socket.c Index: ChangeLog =================================================================== --- ChangeLog (revision 52599) +++ ChangeLog (revision 52600) @@ -1,3 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Tue Nov 17 08:25:57 2015 Eric Wong <e@8...> + + * 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 + Tue Nov 17 08:16:09 2015 Eric Wong <e@8...> * ext/socket/init.c (rsock_s_recvfrom_nonblock): Index: ext/socket/lib/socket.rb =================================================================== --- ext/socket/lib/socket.rb (revision 52599) +++ ext/socket/lib/socket.rb (revision 52600) @@ -980,6 +980,53 @@ class Socket < BasicSocket https://github.com/ruby/ruby/blob/trunk/ext/socket/lib/socket.rb#L980 } 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 Index: ext/socket/socket.c =================================================================== --- ext/socket/socket.c (revision 52599) +++ ext/socket/socket.c (revision 52600) @@ -10,7 +10,7 @@ https://github.com/ruby/ruby/blob/trunk/ext/socket/socket.c#L10 #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) https://github.com/ruby/ruby/blob/trunk/ext/socket/socket.c#L439 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 *a https://github.com/ruby/ruby/blob/trunk/ext/socket/socket.c#L454 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) https://github.com/ruby/ruby/blob/trunk/ext/socket/socket.c#L2065 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) https://github.com/ruby/ruby/blob/trunk/ext/socket/socket.c#L2103 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")); } -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/