[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]