* [PATCH 0/5] avoid kwarg parsing in socket ext
@ 2015-11-13 4:10 Eric Wong
2015-11-13 4:10 ` [PATCH 1/5] socket: avoid arg parsing in rsock_s_recvfrom_nonblock Eric Wong
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-13 4:10 UTC (permalink / raw)
To: spew
ref: [ruby-core:71439] [Feature #11339]
benchmark/bm_accept_nonblock.rb | 17 ++
benchmark/bm_connect_nonblock.rb | 18 ++
benchmark/bm_recvmsg_nonblock.rb | 16 ++
benchmark/bm_sendmsg_nonblock.rb | 16 ++
ext/socket/ancdata.c | 179 +++------------
ext/socket/basicsocket.c | 73 ++----
ext/socket/init.c | 23 +-
ext/socket/lib/socket.rb | 479 +++++++++++++++++++++++++++++++++++++++
ext/socket/rubysocket.h | 26 +--
ext/socket/socket.c | 203 ++---------------
ext/socket/tcpserver.c | 48 +---
ext/socket/udpsocket.c | 63 +----
ext/socket/unixserver.c | 47 +---
13 files changed, 659 insertions(+), 549 deletions(-)
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/5] socket: avoid arg parsing in rsock_s_recvfrom_nonblock
2015-11-13 4:10 [PATCH 0/5] avoid kwarg parsing in socket ext Eric Wong
@ 2015-11-13 4:10 ` Eric Wong
2015-11-13 4:10 ` [PATCH 2/5] socket: Socket#connect_nonblock avoids arg parsing with C API Eric Wong
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-13 4:10 UTC (permalink / raw)
To: spew
* ext/socket/init.c (rsock_s_recvfrom_nonblock):
avoid arg parsing with C API
[ruby-core:71439] [Feature #11339]
* ext/socket/basicsocket.c (bsock_recv_nonblock):
adjust for above change, make private
* ext/socket/socket.c (sock_recvfrom_nonblock): ditto
* ext/socket/udpsocket.c (udp_recvfrom_nonblock): ditto
* ext/socket/lib/socket.rb (BasicSocket#recv_nonblock):
new wrapper for private method, move RDoc
(Socket#recvfrom_nonblock): ditto
(UDPSocket#recvfrom_nonblock): ditto
Note, not adding bm_recv_nonblock.rb to benchmark/ directory
since it is non-portable. It is only in this commit message.
Benchmark results + code
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]
-----------------------------------------------------------
recv_nonblock
require 'socket'
nr = 1000000
msg = 'hello world'
buf = ''
size = msg.bytesize
UNIXSocket.pair(:SEQPACKET) do |a, b|
nr.times do
a.sendmsg(msg)
b.recv_nonblock(size, 0, buf, exception: false)
end
end
-----------------------------------------------------------
raw data:
[["recv_nonblock",
[[1.83511221408844,
1.8703329525887966,
1.8448856547474861,
1.859263762831688,
1.8331583738327026],
[1.5637447573244572,
1.4062932096421719,
1.4247371144592762,
1.4108827747404575,
1.4802536629140377]]]]
Elapsed time: 16.530452496 (sec)
-----------------------------------------------------------
benchmark results:
minimum results in each 5 measurements.
Execution time (sec)
name a b
recv_nonblock 1.833 1.406
Speedup ratio: compare with the result of `a' (greater is better)
name b
recv_nonblock 1.304
---
ext/socket/basicsocket.c | 60 +++--------------
ext/socket/init.c | 13 ++--
ext/socket/lib/socket.rb | 171 +++++++++++++++++++++++++++++++++++++++++++++++
ext/socket/rubysocket.h | 3 +-
ext/socket/socket.c | 72 ++------------------
ext/socket/udpsocket.c | 63 ++---------------
6 files changed, 199 insertions(+), 183 deletions(-)
diff --git a/ext/socket/basicsocket.c b/ext/socket/basicsocket.c
index e5597f0..6bf10e8 100644
--- a/ext/socket/basicsocket.c
+++ b/ext/socket/basicsocket.c
@@ -643,59 +643,11 @@ bsock_recv(int argc, VALUE *argv, VALUE sock)
return rsock_s_recvfrom(sock, argc, argv, RECV_RECV);
}
-/*
- * call-seq:
- * basicsocket.recv_nonblock(maxlen [, flags [, options ]) => mesg
- *
- * Receives up to _maxlen_ bytes from +socket+ using recvfrom(2) after
- * O_NONBLOCK is set for the underlying file descriptor.
- * _flags_ is zero or more of the +MSG_+ options.
- * The result, _mesg_, is the data received.
- *
- * When recvfrom(2) returns 0, Socket#recv_nonblock returns
- * an empty string as data.
- * The meaning depends on the socket: EOF on TCP, empty packet on UDP, etc.
- *
- * === Parameters
- * * +maxlen+ - the number of bytes to receive from the socket
- * * +flags+ - zero or more of the +MSG_+ options
- * * +options+ - keyword hash, supporting `exception: false`
- *
- * === Example
- * serv = TCPServer.new("127.0.0.1", 0)
- * af, port, host, addr = serv.addr
- * c = TCPSocket.new(addr, port)
- * s = serv.accept
- * c.send "aaa", 0
- * begin # emulate blocking recv.
- * p s.recv_nonblock(10) #=> "aaa"
- * rescue IO::WaitReadable
- * IO.select([s])
- * retry
- * end
- *
- * Refer to Socket#recvfrom for the exceptions that may be thrown if the call
- * to _recv_nonblock_ fails.
- *
- * BasicSocket#recv_nonblock may raise any error corresponding to recvfrom(2) failure,
- * including Errno::EWOULDBLOCK.
- *
- * If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
- * it is extended by IO::WaitReadable.
- * So IO::WaitReadable can be used to rescue the exceptions for retrying recv_nonblock.
- *
- * By specifying `exception: false`, the options hash allows you to indicate
- * that recv_nonblock should not raise an IO::WaitWritable exception, but
- * return the symbol :wait_writable instead.
- *
- * === See
- * * Socket#recvfrom
- */
-
+/* :nodoc: */
static VALUE
-bsock_recv_nonblock(int argc, VALUE *argv, VALUE sock)
+bsock_recv_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, VALUE ex)
{
- return rsock_s_recvfrom_nonblock(sock, argc, argv, RECV_RECV);
+ return rsock_s_recvfrom_nonblock(sock, len, flg, str, ex, RECV_RECV);
}
/*
@@ -764,10 +716,14 @@ rsock_init_basicsocket(void)
rb_define_method(rb_cBasicSocket, "remote_address", bsock_remote_address, 0);
rb_define_method(rb_cBasicSocket, "send", rsock_bsock_send, -1);
rb_define_method(rb_cBasicSocket, "recv", bsock_recv, -1);
- rb_define_method(rb_cBasicSocket, "recv_nonblock", bsock_recv_nonblock, -1);
+
rb_define_method(rb_cBasicSocket, "do_not_reverse_lookup", bsock_do_not_reverse_lookup, 0);
rb_define_method(rb_cBasicSocket, "do_not_reverse_lookup=", bsock_do_not_reverse_lookup_set, 1);
+ /* for ext/socket/lib/socket.rb use only: */
+ rb_define_private_method(rb_cBasicSocket,
+ "__recv_nonblock", bsock_recv_nonblock, 4);
+
rb_define_method(rb_cBasicSocket, "sendmsg", rsock_bsock_sendmsg, -1); /* in ancdata.c */
rb_define_method(rb_cBasicSocket, "sendmsg_nonblock", rsock_bsock_sendmsg_nonblock, -1); /* in ancdata.c */
rb_define_method(rb_cBasicSocket, "recvmsg", rsock_bsock_recvmsg, -1); /* in ancdata.c */
diff --git a/ext/socket/init.c b/ext/socket/init.c
index 1710129..350ba65 100644
--- a/ext/socket/init.c
+++ b/ext/socket/init.c
@@ -200,24 +200,19 @@ rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from)
}
VALUE
-rsock_s_recvfrom_nonblock(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from)
+rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
+ VALUE ex, enum sock_recv_type from)
{
rb_io_t *fptr;
- VALUE str;
union_sockaddr buf;
socklen_t alen = (socklen_t)sizeof buf;
- VALUE len, flg;
long buflen;
long slen;
int fd, flags;
VALUE addr = Qnil;
- VALUE opts = Qnil;
socklen_t len0;
- rb_scan_args(argc, argv, "12:", &len, &flg, &str, &opts);
-
- if (flg == Qnil) flags = 0;
- else flags = NUM2INT(flg);
+ flags = NUM2INT(flg);
buflen = NUM2INT(len);
str = rsock_strbuf(str, buflen);
@@ -249,7 +244,7 @@ rsock_s_recvfrom_nonblock(VALUE sock, int argc, VALUE *argv, enum sock_recv_type
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
- if (rsock_opt_false_p(opts, sym_exception))
+ if (ex == Qfalse)
return sym_wait_readable;
rb_readwrite_sys_fail(RB_IO_WAIT_READABLE, "recvfrom(2) would block");
}
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index 7c66e8f..915eb96 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -272,6 +272,56 @@ class BasicSocket < IO
end
addr
end
+
+ # call-seq:
+ # basicsocket.recv_nonblock(maxlen [, flags [, buf [, options ]]]) => mesg
+ #
+ # Receives up to _maxlen_ bytes from +socket+ using recvfrom(2) after
+ # O_NONBLOCK is set for the underlying file descriptor.
+ # _flags_ is zero or more of the +MSG_+ options.
+ # The result, _mesg_, is the data received.
+ #
+ # When recvfrom(2) returns 0, Socket#recv_nonblock returns
+ # an empty string as data.
+ # The meaning depends on the socket: EOF on TCP, empty packet on UDP, etc.
+ #
+ # === Parameters
+ # * +maxlen+ - the number of bytes to receive from the socket
+ # * +flags+ - zero or more of the +MSG_+ options
+ # * +options+ - keyword hash, supporting `exception: false`
+ #
+ # === Example
+ # serv = TCPServer.new("127.0.0.1", 0)
+ # af, port, host, addr = serv.addr
+ # c = TCPSocket.new(addr, port)
+ # s = serv.accept
+ # c.send "aaa", 0
+ # begin # emulate blocking recv.
+ # p s.recv_nonblock(10) #=> "aaa"
+ # rescue IO::WaitReadable
+ # IO.select([s])
+ # retry
+ # end
+ #
+ # Refer to Socket#recvfrom for the exceptions that may be thrown if the call
+ # to _recv_nonblock_ fails.
+ #
+ # BasicSocket#recv_nonblock may raise any error corresponding to recvfrom(2) failure,
+ # including Errno::EWOULDBLOCK.
+ #
+ # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
+ # it is extended by IO::WaitReadable.
+ # So IO::WaitReadable can be used to rescue the exceptions for retrying recv_nonblock.
+ #
+ # By specifying `exception: false`, the options hash allows you to indicate
+ # that recv_nonblock should not raise an IO::WaitWritable exception, but
+ # return the symbol :wait_writable instead.
+ #
+ # === See
+ # * Socket#recvfrom
+ def recv_nonblock(len, flag = 0, str = nil, exception: true)
+ __recv_nonblock(len, flag, str, exception)
+ end
end
class Socket < BasicSocket
@@ -282,6 +332,70 @@ class Socket < BasicSocket
end
end
+ # call-seq:
+ # socket.recvfrom_nonblock(maxlen) => [mesg, sender_addrinfo]
+ # socket.recvfrom_nonblock(maxlen, flags) => [mesg, sender_addrinfo]
+ #
+ # Receives up to _maxlen_ bytes from +socket+ using recvfrom(2) after
+ # O_NONBLOCK is set for the underlying file descriptor.
+ # _flags_ is zero or more of the +MSG_+ options.
+ # The first element of the results, _mesg_, is the data received.
+ # The second element, _sender_addrinfo_, contains protocol-specific address
+ # information of the sender.
+ #
+ # When recvfrom(2) returns 0, Socket#recvfrom_nonblock returns
+ # an empty string as data.
+ # The meaning depends on the socket: EOF on TCP, empty packet on UDP, etc.
+ #
+ # === Parameters
+ # * +maxlen+ - the maximum number of bytes to receive from the socket
+ # * +flags+ - zero or more of the +MSG_+ options
+ #
+ # === Example
+ # # In one file, start this first
+ # require 'socket'
+ # include Socket::Constants
+ # socket = Socket.new(AF_INET, SOCK_STREAM, 0)
+ # sockaddr = Socket.sockaddr_in(2200, 'localhost')
+ # socket.bind(sockaddr)
+ # socket.listen(5)
+ # client, client_addrinfo = socket.accept
+ # begin # emulate blocking recvfrom
+ # pair = client.recvfrom_nonblock(20)
+ # rescue IO::WaitReadable
+ # IO.select([client])
+ # retry
+ # end
+ # data = pair[0].chomp
+ # puts "I only received 20 bytes '#{data}'"
+ # sleep 1
+ # socket.close
+ #
+ # # In another file, start this second
+ # require 'socket'
+ # include Socket::Constants
+ # socket = Socket.new(AF_INET, SOCK_STREAM, 0)
+ # sockaddr = Socket.sockaddr_in(2200, 'localhost')
+ # socket.connect(sockaddr)
+ # socket.puts "Watch this get cut short!"
+ # socket.close
+ #
+ # Refer to Socket#recvfrom for the exceptions that may be thrown if the call
+ # to _recvfrom_nonblock_ fails.
+ #
+ # Socket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure,
+ # including Errno::EWOULDBLOCK.
+ #
+ # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
+ # it is extended by IO::WaitReadable.
+ # So IO::WaitReadable can be used to rescue the exceptions for retrying recvfrom_nonblock.
+ #
+ # === See
+ # * Socket#recvfrom
+ def recvfrom_nonblock(len, flag = 0, str = nil, exception: true)
+ __recvfrom_nonblock(len, flag, str, exception)
+ end
+
# :call-seq:
# Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... }
# Socket.tcp(host, port, local_host=nil, local_port=nil, [opts])
@@ -866,3 +980,60 @@ class Socket < BasicSocket
end
+class UDPSocket < IPSocket
+
+ # call-seq:
+ # udpsocket.recvfrom_nonblock(maxlen [, flags [, options]]) => [mesg, sender_inet_addr]
+ #
+ # Receives up to _maxlen_ bytes from +udpsocket+ using recvfrom(2) after
+ # O_NONBLOCK is set for the underlying file descriptor.
+ # If _maxlen_ is omitted, its default value is 65536.
+ # _flags_ is zero or more of the +MSG_+ options.
+ # The first element of the results, _mesg_, is the data received.
+ # The second element, _sender_inet_addr_, is an array to represent the sender address.
+ #
+ # When recvfrom(2) returns 0,
+ # Socket#recvfrom_nonblock returns an empty string as data.
+ # It means an empty packet.
+ #
+ # === Parameters
+ # * +maxlen+ - the number of bytes to receive from the socket
+ # * +flags+ - zero or more of the +MSG_+ options
+ # * +options+ - keyword hash, supporting `exception: false`
+ #
+ # === Example
+ # require 'socket'
+ # s1 = UDPSocket.new
+ # s1.bind("127.0.0.1", 0)
+ # s2 = UDPSocket.new
+ # s2.bind("127.0.0.1", 0)
+ # s2.connect(*s1.addr.values_at(3,1))
+ # s1.connect(*s2.addr.values_at(3,1))
+ # s1.send "aaa", 0
+ # begin # emulate blocking recvfrom
+ # p s2.recvfrom_nonblock(10) #=> ["aaa", ["AF_INET", 33302, "localhost.localdomain", "127.0.0.1"]]
+ # rescue IO::WaitReadable
+ # IO.select([s2])
+ # retry
+ # end
+ #
+ # Refer to Socket#recvfrom for the exceptions that may be thrown if the call
+ # to _recvfrom_nonblock_ fails.
+ #
+ # UDPSocket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure,
+ # including Errno::EWOULDBLOCK.
+ #
+ # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
+ # it is extended by IO::WaitReadable.
+ # So IO::WaitReadable can be used to rescue the exceptions for retrying recvfrom_nonblock.
+ #
+ # By specifying `exception: false`, the options hash allows you to indicate
+ # that recvmsg_nonblock should not raise an IO::WaitWritable exception, but
+ # return the symbol :wait_writable instead.
+ #
+ # === See
+ # * Socket#recvfrom
+ def recvfrom_nonblock(len, flag = 0, str = nil, exception: true)
+ __recvfrom_nonblock(len, flag, str, exception)
+ end
+end
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index 601667d..9fa16ec 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -347,7 +347,8 @@ enum sock_recv_type {
RECV_SOCKET /* Socket#recvfrom */
};
-VALUE rsock_s_recvfrom_nonblock(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from);
+VALUE rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
+ VALUE ex, enum sock_recv_type from);
VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from);
int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks);
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 779398c..1a3320d 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -813,72 +813,11 @@ sock_recvfrom(int argc, VALUE *argv, VALUE sock)
return rsock_s_recvfrom(sock, argc, argv, RECV_SOCKET);
}
-/*
- * call-seq:
- * socket.recvfrom_nonblock(maxlen) => [mesg, sender_addrinfo]
- * socket.recvfrom_nonblock(maxlen, flags) => [mesg, sender_addrinfo]
- *
- * Receives up to _maxlen_ bytes from +socket+ using recvfrom(2) after
- * O_NONBLOCK is set for the underlying file descriptor.
- * _flags_ is zero or more of the +MSG_+ options.
- * The first element of the results, _mesg_, is the data received.
- * The second element, _sender_addrinfo_, contains protocol-specific address
- * information of the sender.
- *
- * When recvfrom(2) returns 0, Socket#recvfrom_nonblock returns
- * an empty string as data.
- * The meaning depends on the socket: EOF on TCP, empty packet on UDP, etc.
- *
- * === Parameters
- * * +maxlen+ - the maximum number of bytes to receive from the socket
- * * +flags+ - zero or more of the +MSG_+ options
- *
- * === Example
- * # In one file, start this first
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new(AF_INET, SOCK_STREAM, 0)
- * sockaddr = Socket.sockaddr_in(2200, 'localhost')
- * socket.bind(sockaddr)
- * socket.listen(5)
- * client, client_addrinfo = socket.accept
- * begin # emulate blocking recvfrom
- * pair = client.recvfrom_nonblock(20)
- * rescue IO::WaitReadable
- * IO.select([client])
- * retry
- * end
- * data = pair[0].chomp
- * puts "I only received 20 bytes '#{data}'"
- * sleep 1
- * socket.close
- *
- * # In another file, start this second
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new(AF_INET, SOCK_STREAM, 0)
- * sockaddr = Socket.sockaddr_in(2200, 'localhost')
- * socket.connect(sockaddr)
- * socket.puts "Watch this get cut short!"
- * socket.close
- *
- * Refer to Socket#recvfrom for the exceptions that may be thrown if the call
- * to _recvfrom_nonblock_ fails.
- *
- * Socket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure,
- * including Errno::EWOULDBLOCK.
- *
- * If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
- * it is extended by IO::WaitReadable.
- * So IO::WaitReadable can be used to rescue the exceptions for retrying recvfrom_nonblock.
- *
- * === See
- * * Socket#recvfrom
- */
+/* :nodoc: */
static VALUE
-sock_recvfrom_nonblock(int argc, VALUE *argv, VALUE sock)
+sock_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, VALUE ex)
{
- return rsock_s_recvfrom_nonblock(sock, argc, argv, RECV_SOCKET);
+ return rsock_s_recvfrom_nonblock(sock, len, flg, str, ex, RECV_SOCKET);
}
/*
@@ -2182,7 +2121,10 @@ Init_socket(void)
rb_define_method(rb_cSocket, "sysaccept", sock_sysaccept, 0);
rb_define_method(rb_cSocket, "recvfrom", sock_recvfrom, -1);
- rb_define_method(rb_cSocket, "recvfrom_nonblock", sock_recvfrom_nonblock, -1);
+
+ /* for ext/socket/lib/socket.rb use only: */
+ rb_define_private_method(rb_cSocket,
+ "__recvfrom_nonblock", sock_recvfrom_nonblock, 4);
rb_define_singleton_method(rb_cSocket, "socketpair", rsock_sock_s_socketpair, -1);
rb_define_singleton_method(rb_cSocket, "pair", rsock_sock_s_socketpair, -1);
diff --git a/ext/socket/udpsocket.c b/ext/socket/udpsocket.c
index d55ddfe..7b7b34f 100644
--- a/ext/socket/udpsocket.c
+++ b/ext/socket/udpsocket.c
@@ -214,63 +214,11 @@ udp_send(int argc, VALUE *argv, VALUE sock)
return ret;
}
-/*
- * call-seq:
- * udpsocket.recvfrom_nonblock(maxlen [, flags [, options]]) => [mesg, sender_inet_addr]
- *
- * Receives up to _maxlen_ bytes from +udpsocket+ using recvfrom(2) after
- * O_NONBLOCK is set for the underlying file descriptor.
- * If _maxlen_ is omitted, its default value is 65536.
- * _flags_ is zero or more of the +MSG_+ options.
- * The first element of the results, _mesg_, is the data received.
- * The second element, _sender_inet_addr_, is an array to represent the sender address.
- *
- * When recvfrom(2) returns 0,
- * Socket#recvfrom_nonblock returns an empty string as data.
- * It means an empty packet.
- *
- * === Parameters
- * * +maxlen+ - the number of bytes to receive from the socket
- * * +flags+ - zero or more of the +MSG_+ options
- * * +options+ - keyword hash, supporting `exception: false`
- *
- * === Example
- * require 'socket'
- * s1 = UDPSocket.new
- * s1.bind("127.0.0.1", 0)
- * s2 = UDPSocket.new
- * s2.bind("127.0.0.1", 0)
- * s2.connect(*s1.addr.values_at(3,1))
- * s1.connect(*s2.addr.values_at(3,1))
- * s1.send "aaa", 0
- * begin # emulate blocking recvfrom
- * p s2.recvfrom_nonblock(10) #=> ["aaa", ["AF_INET", 33302, "localhost.localdomain", "127.0.0.1"]]
- * rescue IO::WaitReadable
- * IO.select([s2])
- * retry
- * end
- *
- * Refer to Socket#recvfrom for the exceptions that may be thrown if the call
- * to _recvfrom_nonblock_ fails.
- *
- * UDPSocket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure,
- * including Errno::EWOULDBLOCK.
- *
- * If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
- * it is extended by IO::WaitReadable.
- * So IO::WaitReadable can be used to rescue the exceptions for retrying recvfrom_nonblock.
- *
- * By specifying `exception: false`, the options hash allows you to indicate
- * that recvmsg_nonblock should not raise an IO::WaitWritable exception, but
- * return the symbol :wait_writable instead.
- *
- * === See
- * * Socket#recvfrom
- */
+/* :nodoc: */
static VALUE
-udp_recvfrom_nonblock(int argc, VALUE *argv, VALUE sock)
+udp_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, VALUE ex)
{
- return rsock_s_recvfrom_nonblock(sock, argc, argv, RECV_IP);
+ return rsock_s_recvfrom_nonblock(sock, len, flg, str, ex, RECV_IP);
}
void
@@ -287,5 +235,8 @@ rsock_init_udpsocket(void)
rb_define_method(rb_cUDPSocket, "connect", udp_connect, 2);
rb_define_method(rb_cUDPSocket, "bind", udp_bind, 2);
rb_define_method(rb_cUDPSocket, "send", udp_send, -1);
- rb_define_method(rb_cUDPSocket, "recvfrom_nonblock", udp_recvfrom_nonblock, -1);
+
+ /* for ext/socket/lib/socket.rb use only: */
+ rb_define_private_method(rb_cUDPSocket,
+ "__recvfrom_nonblock", udp_recvfrom_nonblock, 4);
}
--
EW
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/5] socket: Socket#connect_nonblock avoids arg parsing with C API
2015-11-13 4:10 [PATCH 0/5] avoid kwarg parsing in socket ext Eric Wong
2015-11-13 4:10 ` [PATCH 1/5] socket: avoid arg parsing in rsock_s_recvfrom_nonblock Eric Wong
@ 2015-11-13 4:10 ` Eric Wong
2015-11-13 4:10 ` [PATCH 3/5] socket: avoid arg parsing in rsock_s_accept_nonblock Eric Wong
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-13 4:10 UTC (permalink / raw)
To: spew
* 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
---
benchmark/bm_connect_nonblock.rb | 18 +++++++++++
ext/socket/lib/socket.rb | 47 +++++++++++++++++++++++++++++
ext/socket/socket.c | 65 +++++++---------------------------------
3 files changed, 75 insertions(+), 55 deletions(-)
create mode 100644 benchmark/bm_connect_nonblock.rb
diff --git a/benchmark/bm_connect_nonblock.rb b/benchmark/bm_connect_nonblock.rb
new file mode 100644
index 0000000..8a4428c
--- /dev/null
+++ b/benchmark/bm_connect_nonblock.rb
@@ -0,0 +1,18 @@
+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
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index 915eb96..1f55323 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -978,6 +978,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 1a3320d..a410625 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"));
}
--
EW
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/5] socket: avoid arg parsing in rsock_s_accept_nonblock
2015-11-13 4:10 [PATCH 0/5] avoid kwarg parsing in socket ext Eric Wong
2015-11-13 4:10 ` [PATCH 1/5] socket: avoid arg parsing in rsock_s_recvfrom_nonblock Eric Wong
2015-11-13 4:10 ` [PATCH 2/5] socket: Socket#connect_nonblock avoids arg parsing with C API Eric Wong
@ 2015-11-13 4:10 ` Eric Wong
2015-11-13 4:10 ` [PATCH 4/5] socket (bsock_recvmsg_internal): avoid arg parsing Eric Wong
2015-11-13 4:10 ` [PATCH 5/5] socket: avoid arg parsing in bsock_sendmsg_internal Eric Wong
4 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-13 4:10 UTC (permalink / raw)
To: spew
* ext/socket/init.c (rsock_s_accept_nonblock): avoid parsing args
[ruby-core:71439] [Feature #11339]
* ext/socket/rubysocket.h: adjust prototype
* ext/socket/socket.c (sock_accept_nonblock): make private
* ext/socket/tcpserver.c (tcp_accept_nonblock): ditto
* ext/socket/unixserver.c (unix_accept_nonblock): ditto
* ext/socket/lib/socket.rb (Socket#accept_nonblock):
implement as wrapper, move RDoc
(TCPServer#accept_nonblock): ditto
(UNIXServer#accept_nonblock): ditto
target 0: a (ruby 2.3.0dev (2015-11-12 trunk 52550) [x86_64-linux])
target 1: b (ruby 2.3.0dev (2015-11-12 avoid-kwarg-capi 52550) [x86_64-linux]
-----------------------------------------------------------
accept_nonblock
require 'tempfile'
require 'socket'
require 'io/wait'
nr = 500000
Tempfile.create(%w(accept_nonblock .sock)) do |tmp|
path = tmp.path
File.unlink(path)
s = UNIXServer.new(path)
addr = Socket.sockaddr_un(path).freeze
nr.times do
s.accept_nonblock(exception: false)
c = UNIXSocket.new(path)
s.wait_readable
s.accept_nonblock(exception: false).close
c.close
end
end
-----------------------------------------------------------
raw data:
[["accept_nonblock",
[[4.807877402752638,
4.930681671947241,
4.738454818725586,
4.69268161803484,
4.684675686061382],
[4.253904823213816,
4.255124930292368,
4.295955188572407,
4.248479191213846,
4.213303029537201]]]]
Elapsed time: 45.123040065 (sec)
-----------------------------------------------------------
benchmark results:
minimum results in each 5 measurements.
Execution time (sec)
name a b
accept_nonblock 4.685 4.213
Speedup ratio: compare with the result of `a' (greater is better)
name b
accept_nonblock 1.112
---
benchmark/bm_accept_nonblock.rb | 17 +++++
ext/socket/init.c | 10 +--
ext/socket/lib/socket.rb | 140 ++++++++++++++++++++++++++++++++++++++++
ext/socket/rubysocket.h | 3 +-
ext/socket/socket.c | 66 +++----------------
ext/socket/tcpserver.c | 48 ++------------
ext/socket/unixserver.c | 47 ++------------
7 files changed, 183 insertions(+), 148 deletions(-)
create mode 100644 benchmark/bm_accept_nonblock.rb
diff --git a/benchmark/bm_accept_nonblock.rb b/benchmark/bm_accept_nonblock.rb
new file mode 100644
index 0000000..8e537f9
--- /dev/null
+++ b/benchmark/bm_accept_nonblock.rb
@@ -0,0 +1,17 @@
+require 'tempfile'
+require 'socket'
+require 'io/wait'
+nr = 500000
+Tempfile.create(%w(accept_nonblock .sock)) do |tmp|
+ path = tmp.path
+ File.unlink(path)
+ s = UNIXServer.new(path)
+ addr = Socket.sockaddr_un(path).freeze
+ nr.times do
+ s.accept_nonblock(exception: false)
+ c = UNIXSocket.new(path)
+ s.wait_readable
+ s.accept_nonblock(exception: false).close
+ c.close
+ end
+end
diff --git a/ext/socket/init.c b/ext/socket/init.c
index 350ba65..45070d5 100644
--- a/ext/socket/init.c
+++ b/ext/socket/init.c
@@ -29,7 +29,7 @@ VALUE rb_cSOCKSSocket;
#endif
int rsock_do_not_reverse_lookup = 1;
-static VALUE sym_exception, sym_wait_readable;
+static VALUE sym_wait_readable;
void
rsock_raise_socket_error(const char *reason, int error)
@@ -544,13 +544,10 @@ cloexec_accept(int socket, struct sockaddr *address, socklen_t *address_len,
}
VALUE
-rsock_s_accept_nonblock(int argc, VALUE *argv, VALUE klass, rb_io_t *fptr,
+rsock_s_accept_nonblock(VALUE klass, VALUE ex, rb_io_t *fptr,
struct sockaddr *sockaddr, socklen_t *len)
{
int fd2;
- VALUE opts = Qnil;
-
- rb_scan_args(argc, argv, "0:", &opts);
rb_io_set_nonblock(fptr);
fd2 = cloexec_accept(fptr->fd, (struct sockaddr*)sockaddr, len, 1);
@@ -564,7 +561,7 @@ rsock_s_accept_nonblock(int argc, VALUE *argv, VALUE klass, rb_io_t *fptr,
#if defined EPROTO
case EPROTO:
#endif
- if (rsock_opt_false_p(opts, sym_exception))
+ if (ex == Qfalse)
return sym_wait_readable;
rb_readwrite_sys_fail(RB_IO_WAIT_READABLE, "accept(2) would block");
}
@@ -673,6 +670,5 @@ rsock_init_socket_init(void)
rsock_init_socket_constants();
#undef rb_intern
- sym_exception = ID2SYM(rb_intern("exception"));
sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
}
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index 1f55323..edee69a 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -396,6 +396,63 @@ class Socket < BasicSocket
__recvfrom_nonblock(len, flag, str, exception)
end
+ # call-seq:
+ # socket.accept_nonblock([options]) => [client_socket, client_addrinfo]
+ #
+ # Accepts an incoming connection using accept(2) after
+ # O_NONBLOCK is set for the underlying file descriptor.
+ # It returns an array containing the accepted socket
+ # for the incoming connection, _client_socket_,
+ # and an Addrinfo, _client_addrinfo_.
+ #
+ # === Example
+ # # In one script, start this first
+ # require 'socket'
+ # include Socket::Constants
+ # socket = Socket.new(AF_INET, SOCK_STREAM, 0)
+ # sockaddr = Socket.sockaddr_in(2200, 'localhost')
+ # socket.bind(sockaddr)
+ # socket.listen(5)
+ # begin # emulate blocking accept
+ # client_socket, client_addrinfo = socket.accept_nonblock
+ # rescue IO::WaitReadable, Errno::EINTR
+ # IO.select([socket])
+ # retry
+ # end
+ # puts "The client said, '#{client_socket.readline.chomp}'"
+ # client_socket.puts "Hello from script one!"
+ # socket.close
+ #
+ # # In another script, start this second
+ # require 'socket'
+ # include Socket::Constants
+ # socket = Socket.new(AF_INET, SOCK_STREAM, 0)
+ # sockaddr = Socket.sockaddr_in(2200, 'localhost')
+ # socket.connect(sockaddr)
+ # socket.puts "Hello from script 2."
+ # puts "The server said, '#{socket.readline.chomp}'"
+ # socket.close
+ #
+ # Refer to Socket#accept for the exceptions that may be thrown if the call
+ # to _accept_nonblock_ fails.
+ #
+ # Socket#accept_nonblock may raise any error corresponding to accept(2) failure,
+ # including Errno::EWOULDBLOCK.
+ #
+ # If the exception is Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED or Errno::EPROTO,
+ # it is extended by IO::WaitReadable.
+ # So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock.
+ #
+ # By specifying `exception: false`, the options hash allows you to indicate
+ # that accept_nonblock should not raise an IO::WaitReadable exception, but
+ # return the symbol :wait_readable instead.
+ #
+ # === See
+ # * Socket#accept
+ def accept_nonblock(exception: true)
+ __accept_nonblock(exception)
+ end
+
# :call-seq:
# Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... }
# Socket.tcp(host, port, local_host=nil, local_port=nil, [opts])
@@ -1084,3 +1141,86 @@ class UDPSocket < IPSocket
__recvfrom_nonblock(len, flag, str, exception)
end
end
+
+class TCPServer < TCPSocket
+
+ # call-seq:
+ # tcpserver.accept_nonblock([options]) => tcpsocket
+ #
+ # Accepts an incoming connection using accept(2) after
+ # O_NONBLOCK is set for the underlying file descriptor.
+ # It returns an accepted TCPSocket for the incoming connection.
+ #
+ # === Example
+ # require 'socket'
+ # serv = TCPServer.new(2202)
+ # begin # emulate blocking accept
+ # sock = serv.accept_nonblock
+ # rescue IO::WaitReadable, Errno::EINTR
+ # IO.select([serv])
+ # retry
+ # end
+ # # sock is an accepted socket.
+ #
+ # Refer to Socket#accept for the exceptions that may be thrown if the call
+ # to TCPServer#accept_nonblock fails.
+ #
+ # TCPServer#accept_nonblock may raise any error corresponding to accept(2) failure,
+ # including Errno::EWOULDBLOCK.
+ #
+ # If the exception is Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO,
+ # it is extended by IO::WaitReadable.
+ # So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock.
+ #
+ # By specifying `exception: false`, the options hash allows you to indicate
+ # that accept_nonblock should not raise an IO::WaitReadable exception, but
+ # return the symbol :wait_readable instead.
+ #
+ # === See
+ # * TCPServer#accept
+ # * Socket#accept
+ def accept_nonblock(exception: true)
+ __accept_nonblock(exception)
+ end
+end
+
+class UNIXServer < UNIXSocket
+ # call-seq:
+ # unixserver.accept_nonblock([options]) => unixsocket
+ #
+ # Accepts an incoming connection using accept(2) after
+ # O_NONBLOCK is set for the underlying file descriptor.
+ # It returns an accepted UNIXSocket for the incoming connection.
+ #
+ # === Example
+ # require 'socket'
+ # serv = UNIXServer.new("/tmp/sock")
+ # begin # emulate blocking accept
+ # sock = serv.accept_nonblock
+ # rescue IO::WaitReadable, Errno::EINTR
+ # IO.select([serv])
+ # retry
+ # end
+ # # sock is an accepted socket.
+ #
+ # Refer to Socket#accept for the exceptions that may be thrown if the call
+ # to UNIXServer#accept_nonblock fails.
+ #
+ # UNIXServer#accept_nonblock may raise any error corresponding to accept(2) failure,
+ # including Errno::EWOULDBLOCK.
+ #
+ # If the exception is Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED or Errno::EPROTO,
+ # it is extended by IO::WaitReadable.
+ # So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock.
+ #
+ # By specifying `exception: false`, the options hash allows you to indicate
+ # that accept_nonblock should not raise an IO::WaitReadable exception, but
+ # return the symbol :wait_readable instead.
+ #
+ # === See
+ # * UNIXServer#accept
+ # * Socket#accept
+ def accept_nonblock(exception: true)
+ __accept_nonblock(exception)
+ end
+end
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index 9fa16ec..d39de0a 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -354,7 +354,8 @@ VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type fr
int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks);
VALUE rsock_s_accept(VALUE klass, int fd, struct sockaddr *sockaddr, socklen_t *len);
-VALUE rsock_s_accept_nonblock(int argc, VALUE *argv, VALUE klass, rb_io_t *fptr, struct sockaddr *sockaddr, socklen_t *len);
+VALUE rsock_s_accept_nonblock(VALUE klass, VALUE ex, rb_io_t *fptr,
+ struct sockaddr *sockaddr, socklen_t *len);
VALUE rsock_sock_listen(VALUE sock, VALUE log);
VALUE rsock_sockopt_new(int family, int level, int optname, VALUE data);
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index a410625..cc5cbe1 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -800,63 +800,9 @@ sock_accept(VALUE sock)
return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, &buf.addr, len));
}
-/*
- * call-seq:
- * socket.accept_nonblock([options]) => [client_socket, client_addrinfo]
- *
- * Accepts an incoming connection using accept(2) after
- * O_NONBLOCK is set for the underlying file descriptor.
- * It returns an array containing the accepted socket
- * for the incoming connection, _client_socket_,
- * and an Addrinfo, _client_addrinfo_.
- *
- * === Example
- * # In one script, start this first
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new(AF_INET, SOCK_STREAM, 0)
- * sockaddr = Socket.sockaddr_in(2200, 'localhost')
- * socket.bind(sockaddr)
- * socket.listen(5)
- * begin # emulate blocking accept
- * client_socket, client_addrinfo = socket.accept_nonblock
- * rescue IO::WaitReadable, Errno::EINTR
- * IO.select([socket])
- * retry
- * end
- * puts "The client said, '#{client_socket.readline.chomp}'"
- * client_socket.puts "Hello from script one!"
- * socket.close
- *
- * # In another script, start this second
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new(AF_INET, SOCK_STREAM, 0)
- * sockaddr = Socket.sockaddr_in(2200, 'localhost')
- * socket.connect(sockaddr)
- * socket.puts "Hello from script 2."
- * puts "The server said, '#{socket.readline.chomp}'"
- * socket.close
- *
- * Refer to Socket#accept for the exceptions that may be thrown if the call
- * to _accept_nonblock_ fails.
- *
- * Socket#accept_nonblock may raise any error corresponding to accept(2) failure,
- * including Errno::EWOULDBLOCK.
- *
- * If the exception is Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED or Errno::EPROTO,
- * it is extended by IO::WaitReadable.
- * So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock.
- *
- * By specifying `exception: false`, the options hash allows you to indicate
- * that accept_nonblock should not raise an IO::WaitReadable exception, but
- * return the symbol :wait_readable instead.
- *
- * === See
- * * Socket#accept
- */
+/* :nodoc: */
static VALUE
-sock_accept_nonblock(int argc, VALUE *argv, VALUE sock)
+sock_accept_nonblock(VALUE sock, VALUE ex)
{
rb_io_t *fptr;
VALUE sock2;
@@ -865,7 +811,7 @@ sock_accept_nonblock(int argc, VALUE *argv, VALUE sock)
socklen_t len = (socklen_t)sizeof buf;
GetOpenFile(sock, fptr);
- sock2 = rsock_s_accept_nonblock(argc, argv, rb_cSocket, fptr, addr, &len);
+ sock2 = rsock_s_accept_nonblock(rb_cSocket, ex, fptr, addr, &len);
if (SYMBOL_P(sock2)) /* :wait_readable */
return sock2;
@@ -2073,7 +2019,11 @@ Init_socket(void)
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);
- rb_define_method(rb_cSocket, "accept_nonblock", sock_accept_nonblock, -1);
+
+ /* for ext/socket/lib/socket.rb use only: */
+ rb_define_private_method(rb_cSocket,
+ "__accept_nonblock", sock_accept_nonblock, 1);
+
rb_define_method(rb_cSocket, "sysaccept", sock_sysaccept, 0);
rb_define_method(rb_cSocket, "recvfrom", sock_recvfrom, -1);
diff --git a/ext/socket/tcpserver.c b/ext/socket/tcpserver.c
index a38faf8..1bbb31a 100644
--- a/ext/socket/tcpserver.c
+++ b/ext/socket/tcpserver.c
@@ -64,53 +64,16 @@ tcp_accept(VALUE sock)
return rsock_s_accept(rb_cTCPSocket, fptr->fd, &from.addr, &fromlen);
}
-/*
- * call-seq:
- * tcpserver.accept_nonblock([options]) => tcpsocket
- *
- * Accepts an incoming connection using accept(2) after
- * O_NONBLOCK is set for the underlying file descriptor.
- * It returns an accepted TCPSocket for the incoming connection.
- *
- * === Example
- * require 'socket'
- * serv = TCPServer.new(2202)
- * begin # emulate blocking accept
- * sock = serv.accept_nonblock
- * rescue IO::WaitReadable, Errno::EINTR
- * IO.select([serv])
- * retry
- * end
- * # sock is an accepted socket.
- *
- * Refer to Socket#accept for the exceptions that may be thrown if the call
- * to TCPServer#accept_nonblock fails.
- *
- * TCPServer#accept_nonblock may raise any error corresponding to accept(2) failure,
- * including Errno::EWOULDBLOCK.
- *
- * If the exception is Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO,
- * it is extended by IO::WaitReadable.
- * So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock.
- *
- * By specifying `exception: false`, the options hash allows you to indicate
- * that accept_nonblock should not raise an IO::WaitReadable exception, but
- * return the symbol :wait_readable instead.
- *
- * === See
- * * TCPServer#accept
- * * Socket#accept
- */
+/* :nodoc: */
static VALUE
-tcp_accept_nonblock(int argc, VALUE *argv, VALUE sock)
+tcp_accept_nonblock(VALUE sock, VALUE ex)
{
rb_io_t *fptr;
union_sockaddr from;
- socklen_t fromlen;
+ socklen_t len = (socklen_t)sizeof(from);
GetOpenFile(sock, fptr);
- fromlen = (socklen_t)sizeof(from);
- return rsock_s_accept_nonblock(argc, argv, rb_cTCPSocket, fptr, &from.addr, &fromlen);
+ return rsock_s_accept_nonblock(rb_cTCPSocket, ex, fptr, &from.addr, &len);
}
/*
@@ -175,7 +138,8 @@ rsock_init_tcpserver(void)
*/
rb_cTCPServer = rb_define_class("TCPServer", rb_cTCPSocket);
rb_define_method(rb_cTCPServer, "accept", tcp_accept, 0);
- rb_define_method(rb_cTCPServer, "accept_nonblock", tcp_accept_nonblock, -1);
+ rb_define_private_method(rb_cTCPServer,
+ "__accept_nonblock", tcp_accept_nonblock, 1);
rb_define_method(rb_cTCPServer, "sysaccept", tcp_sysaccept, 0);
rb_define_method(rb_cTCPServer, "initialize", tcp_svr_init, -1);
rb_define_method(rb_cTCPServer, "listen", rsock_sock_listen, 1); /* in socket.c */
diff --git a/ext/socket/unixserver.c b/ext/socket/unixserver.c
index 532d951..799dcff 100644
--- a/ext/socket/unixserver.c
+++ b/ext/socket/unixserver.c
@@ -57,45 +57,9 @@ unix_accept(VALUE sock)
(struct sockaddr*)&from, &fromlen);
}
-/*
- * call-seq:
- * unixserver.accept_nonblock([options]) => unixsocket
- *
- * Accepts an incoming connection using accept(2) after
- * O_NONBLOCK is set for the underlying file descriptor.
- * It returns an accepted UNIXSocket for the incoming connection.
- *
- * === Example
- * require 'socket'
- * serv = UNIXServer.new("/tmp/sock")
- * begin # emulate blocking accept
- * sock = serv.accept_nonblock
- * rescue IO::WaitReadable, Errno::EINTR
- * IO.select([serv])
- * retry
- * end
- * # sock is an accepted socket.
- *
- * Refer to Socket#accept for the exceptions that may be thrown if the call
- * to UNIXServer#accept_nonblock fails.
- *
- * UNIXServer#accept_nonblock may raise any error corresponding to accept(2) failure,
- * including Errno::EWOULDBLOCK.
- *
- * If the exception is Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED or Errno::EPROTO,
- * it is extended by IO::WaitReadable.
- * So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock.
- *
- * By specifying `exception: false`, the options hash allows you to indicate
- * that accept_nonblock should not raise an IO::WaitReadable exception, but
- * return the symbol :wait_readable instead.
- *
- * === See
- * * UNIXServer#accept
- * * Socket#accept
- */
+/* :nodoc: */
static VALUE
-unix_accept_nonblock(int argc, VALUE *argv, VALUE sock)
+unix_accept_nonblock(VALUE sock, VALUE ex)
{
rb_io_t *fptr;
struct sockaddr_un from;
@@ -103,7 +67,7 @@ unix_accept_nonblock(int argc, VALUE *argv, VALUE sock)
GetOpenFile(sock, fptr);
fromlen = (socklen_t)sizeof(from);
- return rsock_s_accept_nonblock(argc, argv, rb_cUNIXSocket, fptr,
+ return rsock_s_accept_nonblock(rb_cUNIXSocket, ex, fptr,
(struct sockaddr *)&from, &fromlen);
}
@@ -152,7 +116,10 @@ rsock_init_unixserver(void)
rb_cUNIXServer = rb_define_class("UNIXServer", rb_cUNIXSocket);
rb_define_method(rb_cUNIXServer, "initialize", unix_svr_init, 1);
rb_define_method(rb_cUNIXServer, "accept", unix_accept, 0);
- rb_define_method(rb_cUNIXServer, "accept_nonblock", unix_accept_nonblock, -1);
+
+ rb_define_private_method(rb_cUNIXServer,
+ "__accept_nonblock", unix_accept_nonblock, 1);
+
rb_define_method(rb_cUNIXServer, "sysaccept", unix_sysaccept, 0);
rb_define_method(rb_cUNIXServer, "listen", rsock_sock_listen, 1); /* in socket.c */
#endif
--
EW
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 4/5] socket (bsock_recvmsg_internal): avoid arg parsing
2015-11-13 4:10 [PATCH 0/5] avoid kwarg parsing in socket ext Eric Wong
` (2 preceding siblings ...)
2015-11-13 4:10 ` [PATCH 3/5] socket: avoid arg parsing in rsock_s_accept_nonblock Eric Wong
@ 2015-11-13 4:10 ` Eric Wong
2015-11-13 4:10 ` [PATCH 5/5] socket: avoid arg parsing in bsock_sendmsg_internal Eric Wong
4 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-13 4:10 UTC (permalink / raw)
To: spew
* ext/socket/ancdata.c (bsock_recvmsg_internal): avoid arg parsing
(rsock_bsock_recvmsg): adjust for above change
(rsock_bsock_recvmsg_nonblock): ditto
[ruby-core:71439] [Feature #11339]
* ext/socket/rubysocket.h: adjust prototypes for above
* ext/socket/basicsocket.c (rsock_init_basicsocket):
adjust private methods
* ext/socket/lib/socket.rb (BasicSocket#recvmsg): wrapper method
(BasicSocket#recvmsg_nonblock): ditto
target 0: a (ruby 2.3.0dev (2015-11-12 trunk 52550) [x86_64-linux])
target 1: b (ruby 2.3.0dev (2015-11-12 avoid-kwarg-capi 52550) [x86_64-linux]
-----------------------------------------------------------
recvmsg_nonblock
require 'socket'
nr = 1_000_000
i = 0
msg = '.'
buf = '.'
begin
r, w = UNIXSocket.pair(:SEQPACKET)
while i < nr
i += 1
w.sendmsg(msg)
r.recvmsg_nonblock(1, exception: false)
end
ensure
r.close
w.close
end
-----------------------------------------------------------
raw data:
[["recvmsg_nonblock",
[[3.721687912940979,
3.6072621569037437,
3.580637402832508,
3.614185404032469,
3.6029579415917397],
[2.4694008752703667,
2.4908322244882584,
2.5051278844475746,
2.5037173740565777,
2.548359278589487]]]]
Elapsed time: 30.646087052 (sec)
-----------------------------------------------------------
benchmark results:
minimum results in each 5 measurements.
Execution time (sec)
name a b
recvmsg_nonblock 3.581 2.469
Speedup ratio: compare with the result of `a' (greater is better)
name b
recvmsg_nonblock 1.450
---
benchmark/bm_recvmsg_nonblock.rb | 16 +++++++
ext/socket/ancdata.c | 97 +++++++---------------------------------
ext/socket/basicsocket.c | 8 +++-
ext/socket/lib/socket.rb | 71 +++++++++++++++++++++++++++++
ext/socket/rubysocket.h | 6 ++-
5 files changed, 112 insertions(+), 86 deletions(-)
create mode 100644 benchmark/bm_recvmsg_nonblock.rb
diff --git a/benchmark/bm_recvmsg_nonblock.rb b/benchmark/bm_recvmsg_nonblock.rb
new file mode 100644
index 0000000..3c1056b
--- /dev/null
+++ b/benchmark/bm_recvmsg_nonblock.rb
@@ -0,0 +1,16 @@
+require 'socket'
+nr = 1_000_000
+i = 0
+msg = '.'
+buf = '.'
+begin
+ r, w = UNIXSocket.pair(:SEQPACKET)
+ while i < nr
+ i += 1
+ w.sendmsg(msg)
+ r.recvmsg_nonblock(1, exception: false)
+ end
+ensure
+ r.close
+ w.close
+end
diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c
index afa75ef..877975e 100644
--- a/ext/socket/ancdata.c
+++ b/ext/socket/ancdata.c
@@ -1487,11 +1487,11 @@ make_io_for_unix_rights(VALUE ctl, struct cmsghdr *cmh, char *msg_end)
#endif
static VALUE
-bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
+bsock_recvmsg_internal(VALUE sock,
+ VALUE vmaxdatlen, VALUE vflags, VALUE vmaxctllen,
+ VALUE scm_rights, VALUE ex, int nonblock)
{
rb_io_t *fptr;
- VALUE vmaxdatlen, vmaxctllen, vflags;
- VALUE vopts;
int grow_buffer;
size_t maxdatlen;
int flags, orig_flags;
@@ -1512,17 +1512,14 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
int gc_done = 0;
#endif
-
- rb_scan_args(argc, argv, "03:", &vmaxdatlen, &vflags, &vmaxctllen, &vopts);
-
- maxdatlen = NIL_P(vmaxdatlen) ? 4096 : NUM2SIZET(vmaxdatlen);
+ maxdatlen = NUM2SIZET(vmaxdatlen);
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
- maxctllen = NIL_P(vmaxctllen) ? 4096 : NUM2SIZET(vmaxctllen);
+ maxctllen = NUM2SIZET(vmaxctllen);
#else
if (!NIL_P(vmaxctllen))
rb_raise(rb_eArgError, "control message not supported");
#endif
- flags = NIL_P(vflags) ? 0 : NUM2INT(vflags);
+ flags = NUM2INT(vflags);
#ifdef MSG_DONTWAIT
if (nonblock)
flags |= MSG_DONTWAIT;
@@ -1532,7 +1529,7 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
grow_buffer = NIL_P(vmaxdatlen) || NIL_P(vmaxctllen);
request_scm_rights = 0;
- if (!NIL_P(vopts) && RTEST(rb_hash_aref(vopts, ID2SYM(rb_intern("scm_rights")))))
+ if (RTEST(scm_rights))
request_scm_rights = 1;
#if !defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
if (request_scm_rights)
@@ -1602,7 +1599,7 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
goto retry;
}
if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) {
- if (rsock_opt_false_p(vopts, sym_exception)) {
+ if (ex == Qfalse) {
return sym_wait_readable;
}
rb_readwrite_sys_fail(RB_IO_WAIT_READABLE, "recvmsg(2) would block");
@@ -1720,85 +1717,21 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
#endif
#if defined(HAVE_RECVMSG)
-/*
- * call-seq:
- * basicsocket.recvmsg(maxmesglen=nil, flags=0, maxcontrollen=nil, opts={}) => [mesg, sender_addrinfo, rflags, *controls]
- *
- * recvmsg receives a message using recvmsg(2) system call in blocking manner.
- *
- * _maxmesglen_ is the maximum length of mesg to receive.
- *
- * _flags_ is bitwise OR of MSG_* constants such as Socket::MSG_PEEK.
- *
- * _maxcontrollen_ is the maximum length of controls (ancillary data) to receive.
- *
- * _opts_ is option hash.
- * Currently :scm_rights=>bool is the only option.
- *
- * :scm_rights option specifies that application expects SCM_RIGHTS control message.
- * If the value is nil or false, application don't expects SCM_RIGHTS control message.
- * In this case, recvmsg closes the passed file descriptors immediately.
- * This is the default behavior.
- *
- * If :scm_rights value is neither nil nor false, application expects SCM_RIGHTS control message.
- * In this case, recvmsg creates IO objects for each file descriptors for
- * Socket::AncillaryData#unix_rights method.
- *
- * The return value is 4-elements array.
- *
- * _mesg_ is a string of the received message.
- *
- * _sender_addrinfo_ is a sender socket address for connection-less socket.
- * It is an Addrinfo object.
- * For connection-oriented socket such as TCP, sender_addrinfo is platform dependent.
- *
- * _rflags_ is a flags on the received message which is bitwise OR of MSG_* constants such as Socket::MSG_TRUNC.
- * It will be nil if the system uses 4.3BSD style old recvmsg system call.
- *
- * _controls_ is ancillary data which is an array of Socket::AncillaryData objects such as:
- *
- * #<Socket::AncillaryData: AF_UNIX SOCKET RIGHTS 7>
- *
- * _maxmesglen_ and _maxcontrollen_ can be nil.
- * In that case, the buffer will be grown until the message is not truncated.
- * Internally, MSG_PEEK is used and MSG_TRUNC/MSG_CTRUNC are checked.
- *
- * recvmsg can be used to implement recv_io as follows:
- *
- * mesg, sender_sockaddr, rflags, *controls = sock.recvmsg(:scm_rights=>true)
- * controls.each {|ancdata|
- * if ancdata.cmsg_is?(:SOCKET, :RIGHTS)
- * return ancdata.unix_rights[0]
- * end
- * }
- *
- */
VALUE
-rsock_bsock_recvmsg(int argc, VALUE *argv, VALUE sock)
+rsock_bsock_recvmsg(VALUE sock, VALUE dlen, VALUE flags, VALUE clen,
+ VALUE scm_rights)
{
- return bsock_recvmsg_internal(argc, argv, sock, 0);
+ VALUE ex = Qtrue;
+ return bsock_recvmsg_internal(sock, dlen, flags, clen, scm_rights, ex, 0);
}
#endif
#if defined(HAVE_RECVMSG)
-/*
- * call-seq:
- * basicsocket.recvmsg_nonblock(maxdatalen=nil, flags=0, maxcontrollen=nil, opts={}) => [data, sender_addrinfo, rflags, *controls]
- *
- * recvmsg receives a message using recvmsg(2) system call in non-blocking manner.
- *
- * It is similar to BasicSocket#recvmsg
- * but non-blocking flag is set before the system call
- * and it doesn't retry the system call.
- *
- * By specifying `exception: false`, the _opts_ hash allows you to indicate
- * that recvmsg_nonblock should not raise an IO::WaitWritable exception, but
- * return the symbol :wait_writable instead.
- */
VALUE
-rsock_bsock_recvmsg_nonblock(int argc, VALUE *argv, VALUE sock)
+rsock_bsock_recvmsg_nonblock(VALUE sock, VALUE dlen, VALUE flags, VALUE clen,
+ VALUE scm_rights, VALUE ex)
{
- return bsock_recvmsg_internal(argc, argv, sock, 1);
+ return bsock_recvmsg_internal(sock, dlen, flags, clen, scm_rights, ex, 1);
}
#endif
diff --git a/ext/socket/basicsocket.c b/ext/socket/basicsocket.c
index 6bf10e8..1a0120c 100644
--- a/ext/socket/basicsocket.c
+++ b/ext/socket/basicsocket.c
@@ -726,7 +726,11 @@ rsock_init_basicsocket(void)
rb_define_method(rb_cBasicSocket, "sendmsg", rsock_bsock_sendmsg, -1); /* in ancdata.c */
rb_define_method(rb_cBasicSocket, "sendmsg_nonblock", rsock_bsock_sendmsg_nonblock, -1); /* in ancdata.c */
- rb_define_method(rb_cBasicSocket, "recvmsg", rsock_bsock_recvmsg, -1); /* in ancdata.c */
- rb_define_method(rb_cBasicSocket, "recvmsg_nonblock", rsock_bsock_recvmsg_nonblock, -1); /* in ancdata.c */
+
+ /* in ancdata.c */
+ rb_define_private_method(rb_cBasicSocket, "__recvmsg",
+ rsock_bsock_recvmsg, 4);
+ rb_define_private_method(rb_cBasicSocket, "__recvmsg_nonblock",
+ rsock_bsock_recvmsg_nonblock, 5);
}
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index edee69a..65e3d02 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -322,6 +322,77 @@ class BasicSocket < IO
def recv_nonblock(len, flag = 0, str = nil, exception: true)
__recv_nonblock(len, flag, str, exception)
end
+
+ # call-seq:
+ # basicsocket.recvmsg(maxmesglen=nil, flags=0, maxcontrollen=nil, opts={}) => [mesg, sender_addrinfo, rflags, *controls]
+ #
+ # recvmsg receives a message using recvmsg(2) system call in blocking manner.
+ #
+ # _maxmesglen_ is the maximum length of mesg to receive.
+ #
+ # _flags_ is bitwise OR of MSG_* constants such as Socket::MSG_PEEK.
+ #
+ # _maxcontrollen_ is the maximum length of controls (ancillary data) to receive.
+ #
+ # _opts_ is option hash.
+ # Currently :scm_rights=>bool is the only option.
+ #
+ # :scm_rights option specifies that application expects SCM_RIGHTS control message.
+ # If the value is nil or false, application don't expects SCM_RIGHTS control message.
+ # In this case, recvmsg closes the passed file descriptors immediately.
+ # This is the default behavior.
+ #
+ # If :scm_rights value is neither nil nor false, application expects SCM_RIGHTS control message.
+ # In this case, recvmsg creates IO objects for each file descriptors for
+ # Socket::AncillaryData#unix_rights method.
+ #
+ # The return value is 4-elements array.
+ #
+ # _mesg_ is a string of the received message.
+ #
+ # _sender_addrinfo_ is a sender socket address for connection-less socket.
+ # It is an Addrinfo object.
+ # For connection-oriented socket such as TCP, sender_addrinfo is platform dependent.
+ #
+ # _rflags_ is a flags on the received message which is bitwise OR of MSG_* constants such as Socket::MSG_TRUNC.
+ # It will be nil if the system uses 4.3BSD style old recvmsg system call.
+ #
+ # _controls_ is ancillary data which is an array of Socket::AncillaryData objects such as:
+ #
+ # #<Socket::AncillaryData: AF_UNIX SOCKET RIGHTS 7>
+ #
+ # _maxmesglen_ and _maxcontrollen_ can be nil.
+ # In that case, the buffer will be grown until the message is not truncated.
+ # Internally, MSG_PEEK is used and MSG_TRUNC/MSG_CTRUNC are checked.
+ #
+ # recvmsg can be used to implement recv_io as follows:
+ #
+ # mesg, sender_sockaddr, rflags, *controls = sock.recvmsg(:scm_rights=>true)
+ # controls.each {|ancdata|
+ # if ancdata.cmsg_is?(:SOCKET, :RIGHTS)
+ # return ancdata.unix_rights[0]
+ # end
+ # }
+ def recvmsg(dlen = 4096, flags = 0, clen = 4096, scm_rights: false)
+ __recvmsg(dlen, flags, clen, scm_rights)
+ end
+
+ # call-seq:
+ # basicsocket.recvmsg_nonblock(maxdatalen=nil, flags=0, maxcontrollen=nil, opts={}) => [data, sender_addrinfo, rflags, *controls]
+ #
+ # recvmsg receives a message using recvmsg(2) system call in non-blocking manner.
+ #
+ # It is similar to BasicSocket#recvmsg
+ # but non-blocking flag is set before the system call
+ # and it doesn't retry the system call.
+ #
+ # By specifying `exception: false`, the _opts_ hash allows you to indicate
+ # that recvmsg_nonblock should not raise an IO::WaitWritable exception, but
+ # return the symbol :wait_writable instead.
+ def recvmsg_nonblock(dlen = 4096, flags = 0, clen = 4096,
+ scm_rights: false, exception: true)
+ __recvmsg_nonblock(dlen, flags, clen, scm_rights, exception)
+ end
end
class Socket < BasicSocket
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index d39de0a..652876c 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -369,8 +369,10 @@ VALUE rsock_bsock_sendmsg_nonblock(int argc, VALUE *argv, VALUE sock);
#endif
#if defined(HAVE_RECVMSG)
-VALUE rsock_bsock_recvmsg(int argc, VALUE *argv, VALUE sock);
-VALUE rsock_bsock_recvmsg_nonblock(int argc, VALUE *argv, VALUE sock);
+VALUE rsock_bsock_recvmsg(VALUE sock, VALUE dlen, VALUE clen, VALUE flags,
+ VALUE scm_rights);
+VALUE rsock_bsock_recvmsg_nonblock(VALUE sock, VALUE dlen, VALUE clen,
+ VALUE flags, VALUE scm_rights, VALUE ex);
ssize_t rsock_recvmsg(int socket, struct msghdr *message, int flags);
#else
#define rsock_bsock_recvmsg rb_f_notimplement
--
EW
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 5/5] socket: avoid arg parsing in bsock_sendmsg_internal
2015-11-13 4:10 [PATCH 0/5] avoid kwarg parsing in socket ext Eric Wong
` (3 preceding siblings ...)
2015-11-13 4:10 ` [PATCH 4/5] socket (bsock_recvmsg_internal): avoid arg parsing Eric Wong
@ 2015-11-13 4:10 ` Eric Wong
4 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-13 4:10 UTC (permalink / raw)
To: spew
* ext/socket/ancdata.c (bsock_sendmsg_internal): avoid arg parsing
[ruby-core:71439] [Feature #11339]
(rsock_bsock_sendmsg): make private, adjust for above
(rsock_bsock_sendmsg_nonblock): ditto
* ext/socket/rubysocket.h: adjust prototypes
(rsock_opt_false_p): remove
* ext/socket/basicsocket.c (rsock_init_basicsocket):
define private methods
* ext/socket/lib/socket.rb (BasicSocket#sendmsg): new wrapper
(BasicSocket#sendmsg_nonblock): ditto
target 0: a (ruby 2.3.0dev (2015-11-12 trunk 52550) [x86_64-linux])
target 1: b (ruby 2.3.0dev (2015-11-12 avoid-kwarg-capi 52550) [x86_64-linux]
-----------------------------------------------------------
sendmsg_nonblock
require 'socket'
nr = 1_000_000
i = 0
msg = '.'
buf = '.'
begin
r, w = UNIXSocket.pair(:SEQPACKET)
while i < nr
i += 1
w.sendmsg_nonblock(msg, exception: false)
r.recv(1, 0, buf)
end
ensure
r.close
w.close
end
-----------------------------------------------------------
raw data:
[["sendmsg_nonblock",
[[1.875997293740511,
1.8452614955604076,
1.8449317328631878,
1.8418389447033405,
1.869386937469244],
[1.5175109766423702,
1.4987873211503029,
1.4989623799920082,
1.47918451577425,
1.5017359890043736]]]]
Elapsed time: 16.775453245 (sec)
-----------------------------------------------------------
benchmark results:
minimum results in each 5 measurements.
Execution time (sec)
name a b
sendmsg_nonblock 1.842 1.479
Speedup ratio: compare with the result of `a' (greater is better)
name b
sendmsg_nonblock 1.245
---
benchmark/bm_sendmsg_nonblock.rb | 16 ++++++++
ext/socket/ancdata.c | 82 +++++++++-------------------------------
ext/socket/basicsocket.c | 7 ++--
ext/socket/lib/socket.rb | 50 ++++++++++++++++++++++++
ext/socket/rubysocket.h | 14 ++-----
5 files changed, 91 insertions(+), 78 deletions(-)
create mode 100644 benchmark/bm_sendmsg_nonblock.rb
diff --git a/benchmark/bm_sendmsg_nonblock.rb b/benchmark/bm_sendmsg_nonblock.rb
new file mode 100644
index 0000000..a6d3b47
--- /dev/null
+++ b/benchmark/bm_sendmsg_nonblock.rb
@@ -0,0 +1,16 @@
+require 'socket'
+nr = 1_000_000
+i = 0
+msg = '.'
+buf = '.'
+begin
+ r, w = UNIXSocket.pair(:SEQPACKET)
+ while i < nr
+ i += 1
+ w.sendmsg_nonblock(msg, exception: false)
+ r.recv(1, 0, buf)
+ end
+ensure
+ r.close
+ w.close
+end
diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c
index 877975e..7e0441c 100644
--- a/ext/socket/ancdata.c
+++ b/ext/socket/ancdata.c
@@ -3,7 +3,7 @@
#include <time.h>
int rsock_cmsg_cloexec_state = -1; /* <0: unknown, 0: ignored, >0: working */
-static VALUE sym_exception, sym_wait_readable, sym_wait_writable;
+static VALUE sym_wait_readable, sym_wait_writable;
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
static VALUE rb_cAncillaryData;
@@ -1128,14 +1128,13 @@ rb_sendmsg(int fd, const struct msghdr *msg, int flags)
}
static VALUE
-bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
+bsock_sendmsg_internal(VALUE sock, VALUE data, VALUE vflags,
+ VALUE dest_sockaddr, VALUE controls, VALUE ex,
+ int nonblock)
{
rb_io_t *fptr;
- VALUE data, vflags, dest_sockaddr;
struct msghdr mh;
struct iovec iov;
- VALUE opts = Qnil;
- VALUE controls = Qnil;
int controls_num;
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
VALUE controls_str = 0;
@@ -1149,15 +1148,11 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
family = rsock_getfamily(fptr);
#endif
- data = vflags = dest_sockaddr = Qnil;
-
- if (argc == 0)
- rb_raise(rb_eArgError, "mesg argument required");
-
- rb_scan_args(argc, argv, "12*:", &data, &vflags, &dest_sockaddr, &controls,
- &opts);
-
StringValue(data);
+
+ if (!RB_TYPE_P(controls, T_ARRAY)) {
+ controls = rb_ary_new();
+ }
controls_num = RARRAY_LENINT(controls);
if (controls_num) {
@@ -1285,7 +1280,7 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
goto retry;
}
if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) {
- if (rsock_opt_false_p(opts, sym_exception)) {
+ if (ex == Qfalse) {
return sym_wait_writable;
}
rb_readwrite_sys_fail(RB_IO_WAIT_WRITABLE,
@@ -1302,64 +1297,22 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
#endif
#if defined(HAVE_SENDMSG)
-/*
- * call-seq:
- * basicsocket.sendmsg(mesg, flags=0, dest_sockaddr=nil, *controls) => numbytes_sent
- *
- * sendmsg sends a message using sendmsg(2) system call in blocking manner.
- *
- * _mesg_ is a string to send.
- *
- * _flags_ is bitwise OR of MSG_* constants such as Socket::MSG_OOB.
- *
- * _dest_sockaddr_ is a destination socket address for connection-less socket.
- * It should be a sockaddr such as a result of Socket.sockaddr_in.
- * An Addrinfo object can be used too.
- *
- * _controls_ is a list of ancillary data.
- * The element of _controls_ should be Socket::AncillaryData or
- * 3-elements array.
- * The 3-element array should contains cmsg_level, cmsg_type and data.
- *
- * The return value, _numbytes_sent_ is an integer which is the number of bytes sent.
- *
- * sendmsg can be used to implement send_io as follows:
- *
- * # use Socket::AncillaryData.
- * ancdata = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, io.fileno)
- * sock.sendmsg("a", 0, nil, ancdata)
- *
- * # use 3-element array.
- * ancdata = [:SOCKET, :RIGHTS, [io.fileno].pack("i!")]
- * sock.sendmsg("\0", 0, nil, ancdata)
- *
- */
VALUE
-rsock_bsock_sendmsg(int argc, VALUE *argv, VALUE sock)
+rsock_bsock_sendmsg(VALUE sock, VALUE data, VALUE flags, VALUE dest_sockaddr,
+ VALUE controls)
{
- return bsock_sendmsg_internal(argc, argv, sock, 0);
+ return bsock_sendmsg_internal(sock, data, flags, dest_sockaddr, controls,
+ Qtrue, 0);
}
#endif
#if defined(HAVE_SENDMSG)
-/*
- * call-seq:
- * basicsocket.sendmsg_nonblock(mesg, flags=0, dest_sockaddr=nil, *controls, opts={}) => numbytes_sent
- *
- * sendmsg_nonblock sends a message using sendmsg(2) system call in non-blocking manner.
- *
- * It is similar to BasicSocket#sendmsg
- * but the non-blocking flag is set before the system call
- * and it doesn't retry the system call.
- *
- * By specifying `exception: false`, the _opts_ hash allows you to indicate
- * that sendmsg_nonblock should not raise an IO::WaitWritable exception, but
- * return the symbol :wait_writable instead.
- */
VALUE
-rsock_bsock_sendmsg_nonblock(int argc, VALUE *argv, VALUE sock)
+rsock_bsock_sendmsg_nonblock(VALUE sock, VALUE data, VALUE flags,
+ VALUE dest_sockaddr, VALUE controls, VALUE ex)
{
- return bsock_sendmsg_internal(argc, argv, sock, 1);
+ return bsock_sendmsg_internal(sock, data, flags, dest_sockaddr,
+ controls, ex, 1);
}
#endif
@@ -1773,7 +1726,6 @@ rsock_init_ancdata(void)
rb_define_method(rb_cAncillaryData, "ipv6_pktinfo_ifindex", ancillary_ipv6_pktinfo_ifindex, 0);
#endif
#undef rb_intern
- sym_exception = ID2SYM(rb_intern("exception"));
sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
}
diff --git a/ext/socket/basicsocket.c b/ext/socket/basicsocket.c
index 1a0120c..eef66d4 100644
--- a/ext/socket/basicsocket.c
+++ b/ext/socket/basicsocket.c
@@ -724,10 +724,11 @@ rsock_init_basicsocket(void)
rb_define_private_method(rb_cBasicSocket,
"__recv_nonblock", bsock_recv_nonblock, 4);
- rb_define_method(rb_cBasicSocket, "sendmsg", rsock_bsock_sendmsg, -1); /* in ancdata.c */
- rb_define_method(rb_cBasicSocket, "sendmsg_nonblock", rsock_bsock_sendmsg_nonblock, -1); /* in ancdata.c */
-
/* in ancdata.c */
+ rb_define_private_method(rb_cBasicSocket, "__sendmsg",
+ rsock_bsock_sendmsg, 4);
+ rb_define_private_method(rb_cBasicSocket, "__sendmsg_nonblock",
+ rsock_bsock_sendmsg_nonblock, 5);
rb_define_private_method(rb_cBasicSocket, "__recvmsg",
rsock_bsock_recvmsg, 4);
rb_define_private_method(rb_cBasicSocket, "__recvmsg_nonblock",
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index 65e3d02..bc3d38a 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -274,6 +274,56 @@ class BasicSocket < IO
end
# call-seq:
+ # basicsocket.sendmsg(mesg, flags=0, dest_sockaddr=nil, *controls) => numbytes_sent
+ #
+ # sendmsg sends a message using sendmsg(2) system call in blocking manner.
+ #
+ # _mesg_ is a string to send.
+ #
+ # _flags_ is bitwise OR of MSG_* constants such as Socket::MSG_OOB.
+ #
+ # _dest_sockaddr_ is a destination socket address for connection-less socket.
+ # It should be a sockaddr such as a result of Socket.sockaddr_in.
+ # An Addrinfo object can be used too.
+ #
+ # _controls_ is a list of ancillary data.
+ # The element of _controls_ should be Socket::AncillaryData or
+ # 3-elements array.
+ # The 3-element array should contains cmsg_level, cmsg_type and data.
+ #
+ # The return value, _numbytes_sent_ is an integer which is the number of bytes sent.
+ #
+ # sendmsg can be used to implement send_io as follows:
+ #
+ # # use Socket::AncillaryData.
+ # ancdata = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, io.fileno)
+ # sock.sendmsg("a", 0, nil, ancdata)
+ #
+ # # use 3-element array.
+ # ancdata = [:SOCKET, :RIGHTS, [io.fileno].pack("i!")]
+ # sock.sendmsg("\0", 0, nil, ancdata)
+ def sendmsg(mesg, flags = 0, dest_sockaddr = nil, *controls)
+ __sendmsg(mesg, flags, dest_sockaddr, controls)
+ end
+
+ # call-seq:
+ # basicsocket.sendmsg_nonblock(mesg, flags=0, dest_sockaddr=nil, *controls, opts={}) => numbytes_sent
+ #
+ # sendmsg_nonblock sends a message using sendmsg(2) system call in non-blocking manner.
+ #
+ # It is similar to BasicSocket#sendmsg
+ # but the non-blocking flag is set before the system call
+ # and it doesn't retry the system call.
+ #
+ # By specifying `exception: false`, the _opts_ hash allows you to indicate
+ # that sendmsg_nonblock should not raise an IO::WaitWritable exception, but
+ # return the symbol :wait_writable instead.
+ def sendmsg_nonblock(mesg, flags = 0, dest_sockaddr = nil, *controls,
+ exception: true)
+ __sendmsg_nonblock(mesg, flags, dest_sockaddr, controls, exception)
+ end
+
+ # call-seq:
# basicsocket.recv_nonblock(maxlen [, flags [, buf [, options ]]]) => mesg
#
# Receives up to _maxlen_ bytes from +socket+ using recvfrom(2) after
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index 652876c..2135ea4 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -361,8 +361,10 @@ VALUE rsock_sock_listen(VALUE sock, VALUE log);
VALUE rsock_sockopt_new(int family, int level, int optname, VALUE data);
#if defined(HAVE_SENDMSG)
-VALUE rsock_bsock_sendmsg(int argc, VALUE *argv, VALUE sock);
-VALUE rsock_bsock_sendmsg_nonblock(int argc, VALUE *argv, VALUE sock);
+VALUE rsock_bsock_sendmsg(VALUE sock, VALUE data, VALUE flags,
+ VALUE dest_sockaddr, VALUE controls);
+VALUE rsock_bsock_sendmsg_nonblock(VALUE sock, VALUE data, VALUE flags,
+ VALUE dest_sockaddr, VALUE controls, VALUE ex);
#else
#define rsock_bsock_sendmsg rb_f_notimplement
#define rsock_bsock_sendmsg_nonblock rb_f_notimplement
@@ -437,12 +439,4 @@ static inline void rsock_maybe_wait_fd(int fd) { }
# define MSG_DONTWAIT_RELIABLE 0
#endif
-static inline int
-rsock_opt_false_p(VALUE opt, VALUE sym)
-{
- if (!NIL_P(opt) && Qfalse == rb_hash_lookup2(opt, sym, Qundef))
- return 1;
- return 0;
-}
-
#endif
--
EW
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2015-11-13 4:10 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-13 4:10 [PATCH 0/5] avoid kwarg parsing in socket ext Eric Wong
2015-11-13 4:10 ` [PATCH 1/5] socket: avoid arg parsing in rsock_s_recvfrom_nonblock Eric Wong
2015-11-13 4:10 ` [PATCH 2/5] socket: Socket#connect_nonblock avoids arg parsing with C API Eric Wong
2015-11-13 4:10 ` [PATCH 3/5] socket: avoid arg parsing in rsock_s_accept_nonblock Eric Wong
2015-11-13 4:10 ` [PATCH 4/5] socket (bsock_recvmsg_internal): avoid arg parsing Eric Wong
2015-11-13 4:10 ` [PATCH 5/5] socket: avoid arg parsing in bsock_sendmsg_internal Eric Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).