diff options
author | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2015-06-15 19:38:49 +0000 |
---|---|---|
committer | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2015-06-15 19:38:49 +0000 |
commit | 4463ca4c4dba623c9bd73e4615b6574c6ec132e9 (patch) | |
tree | 2c2943128de42db8c8eba898a1c402c7d8bf8ca1 /ext/socket/ancdata.c | |
parent | a94a984089b9815cbf5255d27c3d7c37d8e907b6 (diff) | |
download | ruby-4463ca4c4dba623c9bd73e4615b6574c6ec132e9.tar.gz |
socket: allow exception-free nonblocking sendmsg/recvmsg
As documented before, exceptions are expensive and IO::Wait*able are too
common in socket applications to be the exceptional case. Datagram
sockets deserve the same API which stream sockets are allowed with
read_nonblock and write_nonblock.
Note: this does not offer a performance advantage under optimal
conditions when both ends are equally matched in speed, but it it
does make debug output cleaner by avoiding exceptions whenever
the receiver slows down.
* ext/socket/ancdata.c (bsock_sendmsg_internal, bsock_recvmsg_internal):
support "exception: false" kwarg
* ext/socket/init.c (rsock_s_recvfrom_nonblock):
ditto
* ext/socket/init.c (rsock_s_recvfrom_nonblock): use rsock_opt_false_p
* ext/socket/socket.c (sock_connect_nonblock): ditto
* ext/socket/rubysocket.h (rsock_opt_false_p): new function
* ext/socket/basicsocket.c (bsock_recv_nonblock): update rdoc
* ext/socket/udpsocket.c (udp_recvfrom_nonblock): ditto
* test/socket/test_nonblock.rb: new tests
[ruby-core:69542] [Feature #11229]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50910 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/socket/ancdata.c')
-rw-r--r-- | ext/socket/ancdata.c | 32 |
1 files changed, 27 insertions, 5 deletions
diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c index 6e59694723..614c8f31de 100644 --- a/ext/socket/ancdata.c +++ b/ext/socket/ancdata.c @@ -3,6 +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; #if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) static VALUE rb_cAncillaryData; @@ -1133,6 +1134,7 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock) 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) @@ -1152,7 +1154,8 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock) if (argc == 0) rb_raise(rb_eArgError, "mesg argument required"); - rb_scan_args(argc, argv, "12*", &data, &vflags, &dest_sockaddr, &controls); + rb_scan_args(argc, argv, "12*:", &data, &vflags, &dest_sockaddr, &controls, + &opts); StringValue(data); controls_num = RARRAY_LENINT(controls); @@ -1281,8 +1284,13 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock) rb_io_check_closed(fptr); goto retry; } - if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) - rb_readwrite_sys_fail(RB_IO_WAIT_WRITABLE, "sendmsg(2) would block"); + if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) { + if (rsock_opt_false_p(opts, sym_exception)) { + return sym_wait_writable; + } + rb_readwrite_sys_fail(RB_IO_WAIT_WRITABLE, + "sendmsg(2) would block"); + } rb_sys_fail("sendmsg(2)"); } #if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) @@ -1336,7 +1344,7 @@ rsock_bsock_sendmsg(int argc, VALUE *argv, VALUE sock) #if defined(HAVE_SENDMSG) /* * call-seq: - * basicsocket.sendmsg_nonblock(mesg, flags=0, dest_sockaddr=nil, *controls) => numbytes_sent + * 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. * @@ -1344,6 +1352,9 @@ rsock_bsock_sendmsg(int argc, VALUE *argv, VALUE sock) * 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) @@ -1602,8 +1613,12 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock) rb_io_check_closed(fptr); goto retry; } - if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) + if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) { + if (rsock_opt_false_p(vopts, sym_exception)) { + return sym_wait_readable; + } rb_readwrite_sys_fail(RB_IO_WAIT_READABLE, "recvmsg(2) would block"); + } #if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) if (!gc_done && (errno == EMFILE || errno == EMSGSIZE)) { /* @@ -1788,6 +1803,9 @@ rsock_bsock_recvmsg(int argc, VALUE *argv, VALUE sock) * 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) @@ -1833,4 +1851,8 @@ rsock_init_ancdata(void) rb_define_method(rb_cAncillaryData, "ipv6_pktinfo_addr", ancillary_ipv6_pktinfo_addr, 0); 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")); } |