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
This commit is contained in:
normal 2015-11-16 23:34:37 +00:00
Родитель b272f94f9e
Коммит bb6dfab2a8
3 изменённых файлов: 65 добавлений и 55 удалений

Просмотреть файл

@ -1,3 +1,11 @@
Tue Nov 17 08:25:57 2015 Eric Wong <e@80x24.org>
* 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@80x24.org>
* ext/socket/init.c (rsock_s_recvfrom_nonblock):

Просмотреть файл

@ -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

Просмотреть файл

@ -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"));
}