ruby-changes:40521
From: normal <ko1@a...>
Date: Tue, 17 Nov 2015 09:58:53 +0900 (JST)
Subject: [ruby-changes:40521] normal:r52602 (trunk): socket (bsock_recvmsg_internal): avoid arg parsing
normal 2015-11-17 09:58:23 +0900 (Tue, 17 Nov 2015) New Revision: 52602 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=52602 Log: socket (bsock_recvmsg_internal): avoid arg parsing * 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 Modified files: trunk/ChangeLog trunk/ext/socket/ancdata.c trunk/ext/socket/basicsocket.c trunk/ext/socket/lib/socket.rb trunk/ext/socket/rubysocket.h Index: ChangeLog =================================================================== --- ChangeLog (revision 52601) +++ ChangeLog (revision 52602) @@ -1,3 +1,15 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Tue Nov 17 09:45:18 2015 Eric Wong <e@8...> + + * 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 + Tue Nov 17 08:36:34 2015 Eric Wong <e@8...> * ext/socket/init.c (rsock_s_accept_nonblock): avoid parsing args Index: ext/socket/rubysocket.h =================================================================== --- ext/socket/rubysocket.h (revision 52601) +++ ext/socket/rubysocket.h (revision 52602) @@ -369,8 +369,10 @@ VALUE rsock_bsock_sendmsg_nonblock(int a https://github.com/ruby/ruby/blob/trunk/ext/socket/rubysocket.h#L369 #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 Index: ext/socket/lib/socket.rb =================================================================== --- ext/socket/lib/socket.rb (revision 52601) +++ ext/socket/lib/socket.rb (revision 52602) @@ -324,6 +324,77 @@ class BasicSocket < IO https://github.com/ruby/ruby/blob/trunk/ext/socket/lib/socket.rb#L324 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 Index: ext/socket/basicsocket.c =================================================================== --- ext/socket/basicsocket.c (revision 52601) +++ ext/socket/basicsocket.c (revision 52602) @@ -726,7 +726,11 @@ rsock_init_basicsocket(void) https://github.com/ruby/ruby/blob/trunk/ext/socket/basicsocket.c#L726 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); } Index: ext/socket/ancdata.c =================================================================== --- ext/socket/ancdata.c (revision 52601) +++ ext/socket/ancdata.c (revision 52602) @@ -1487,11 +1487,11 @@ make_io_for_unix_rights(VALUE ctl, struc https://github.com/ruby/ruby/blob/trunk/ext/socket/ancdata.c#L1487 #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 * https://github.com/ruby/ruby/blob/trunk/ext/socket/ancdata.c#L1512 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 * https://github.com/ruby/ruby/blob/trunk/ext/socket/ancdata.c#L1529 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 * https://github.com/ruby/ruby/blob/trunk/ext/socket/ancdata.c#L1599 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 * https://github.com/ruby/ruby/blob/trunk/ext/socket/ancdata.c#L1717 #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 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/