From 3c669e2be96b2584c8dd713fd4d2bd44bad11f9f Mon Sep 17 00:00:00 2001 From: akr Date: Thu, 19 Mar 2009 11:40:38 +0000 Subject: * io.c (rb_mWaitReadable): defined. (rb_mWaitWritable): defined. (io_getpartial): extend IO::WaitReadable on EWOULDBLOCK and EAGAIN. (rb_io_write_nonblock): extend IO::WaitWritable on EWOULDBLOCK and EAGAIN. * error.c (make_errno_exc): extracted from rb_sys_fail. (rb_mod_sys_fail): new function. * include/ruby/ruby.h (rb_mod_sys_fail): declared. (rb_mWaitReadable): declared. (rb_mWaitWritable): declared. * ext/socket/init.c (rsock_s_recvfrom_nonblock): extend IO::WaitReadable on EWOULDBLOCK and EAGAIN. (rsock_s_accept_nonblock): extend IO::WaitReadable on EWOULDBLOCK, EAGAIN, ECONNABORTED and EPROTO. * ext/socket/socket.c (sock_connect_nonblock): extend IO::WaitWritable on EINPROGRESS. * ext/socket/ancdata.c (bsock_sendmsg_internal): extend IO::WaitWritable on EWOULDBLOCK and EAGAIN. (bsock_recvmsg_internal): extend IO::WaitReadable on EWOULDBLOCK and EAGAIN. * ext/openssl/ossl_ssl.c (ossl_ssl_read_internal): raise SSLError extended by IO::WaitReadable/IO::WaitWritable on SSL_ERROR_WANT_READ/SSL_ERROR_WANT_WRITE. * ext/openssl/ossl.c (ossl_make_error): extracted from ossl_raise. (ossl_exc_new): new function. * ext/openssl/ossl.h (ossl_exc_new): declared. * lib/net/protocol.rb (rbuf_fill): rescue IO::WaitReadable and IO::WaitWritable. [ruby-core:22539], [ruby-dev:38140] git-svn-id: http://svn.ruby-lang.org/repos/ruby/trunk@23006 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 42 ++++++++++++++++++++++++++++++++++++++ error.c | 20 +++++++++++++++--- ext/openssl/ossl.c | 31 ++++++++++++++++++++++------ ext/openssl/ossl.h | 1 + ext/openssl/ossl_ssl.c | 10 +++++---- ext/socket/ancdata.c | 8 ++++---- ext/socket/basicsocket.c | 12 +++++++++-- ext/socket/init.c | 4 ++-- ext/socket/socket.c | 20 ++++++++++++++---- ext/socket/tcpserver.c | 6 +++++- ext/socket/udpsocket.c | 12 +++++++++-- ext/socket/unixserver.c | 6 +++++- include/ruby/ruby.h | 3 +++ io.c | 29 ++++++++++++++++++-------- lib/net/protocol.rb | 10 ++++++++- test/openssl/test_pair.rb | 4 ++-- test/openssl/test_ssl.rb | 4 ++-- test/ruby/test_io.rb | 4 ++-- test/socket/test_addrinfo.rb | 4 ++-- test/socket/test_nonblock.rb | 48 ++++++++++++++++++++++---------------------- 20 files changed, 208 insertions(+), 70 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4564c108e..56680ff70 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,45 @@ +Thu Mar 19 20:29:40 2009 Tanaka Akira + + * io.c (rb_mWaitReadable): defined. + (rb_mWaitWritable): defined. + (io_getpartial): extend IO::WaitReadable on EWOULDBLOCK and EAGAIN. + (rb_io_write_nonblock): extend IO::WaitWritable on EWOULDBLOCK and + EAGAIN. + + * error.c (make_errno_exc): extracted from rb_sys_fail. + (rb_mod_sys_fail): new function. + + * include/ruby/ruby.h (rb_mod_sys_fail): declared. + (rb_mWaitReadable): declared. + (rb_mWaitWritable): declared. + + * ext/socket/init.c (rsock_s_recvfrom_nonblock): extend + IO::WaitReadable on EWOULDBLOCK and EAGAIN. + (rsock_s_accept_nonblock): extend IO::WaitReadable on EWOULDBLOCK, + EAGAIN, ECONNABORTED and EPROTO. + + * ext/socket/socket.c (sock_connect_nonblock): extend IO::WaitWritable + on EINPROGRESS. + + * ext/socket/ancdata.c (bsock_sendmsg_internal): extend + IO::WaitWritable on EWOULDBLOCK and EAGAIN. + (bsock_recvmsg_internal): extend IO::WaitReadable on EWOULDBLOCK and + EAGAIN. + + * ext/openssl/ossl_ssl.c (ossl_ssl_read_internal): raise SSLError + extended by IO::WaitReadable/IO::WaitWritable on + SSL_ERROR_WANT_READ/SSL_ERROR_WANT_WRITE. + + * ext/openssl/ossl.c (ossl_make_error): extracted from ossl_raise. + (ossl_exc_new): new function. + + * ext/openssl/ossl.h (ossl_exc_new): declared. + + * lib/net/protocol.rb (rbuf_fill): rescue IO::WaitReadable and + IO::WaitWritable. + + [ruby-core:22539], [ruby-dev:38140] + Wed Mar 18 16:59:48 2009 Kazuhiro NISHIYAMA * test/test_syslog.rb (TestSyslog#test_open): check diff --git a/error.c b/error.c index a5130471b..4c23894f3 100644 --- a/error.c +++ b/error.c @@ -1131,8 +1131,8 @@ rb_fatal(const char *fmt, ...) rb_exc_fatal(rb_exc_new3(rb_eFatal, mesg)); } -void -rb_sys_fail(const char *mesg) +static VALUE +make_errno_exc(const char *mesg) { int n = errno; VALUE arg; @@ -1143,7 +1143,21 @@ rb_sys_fail(const char *mesg) } arg = mesg ? rb_str_new2(mesg) : Qnil; - rb_exc_raise(rb_class_new_instance(1, &arg, get_syserr(n))); + return rb_class_new_instance(1, &arg, get_syserr(n)); +} + +void +rb_sys_fail(const char *mesg) +{ + rb_exc_raise(make_errno_exc(mesg)); +} + +void +rb_mod_sys_fail(VALUE mod, const char *mesg) +{ + VALUE exc = make_errno_exc(mesg); + rb_extend_object(exc, mod); + rb_exc_raise(exc); } void diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 28696cabe..d4a2dc127 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -272,10 +272,9 @@ ossl_to_der_if_possible(VALUE obj) /* * Errors */ -void -ossl_raise(VALUE exc, const char *fmt, ...) +static VALUE +ossl_make_error(VALUE exc, const char *fmt, va_list args) { - va_list args; char buf[BUFSIZ]; const char *msg; long e; @@ -287,9 +286,7 @@ ossl_raise(VALUE exc, const char *fmt, ...) e = ERR_peek_error(); #endif if (fmt) { - va_start(args, fmt); len = vsnprintf(buf, BUFSIZ, fmt, args); - va_end(args); } if (len < BUFSIZ && e) { if (dOSSL == Qtrue) /* FULL INFO */ @@ -306,7 +303,29 @@ ossl_raise(VALUE exc, const char *fmt, ...) ERR_clear_error(); if(len > BUFSIZ) len = strlen(buf); - rb_exc_raise(rb_exc_new(exc, buf, len)); + return rb_exc_new(exc, buf, len); +} + +void +ossl_raise(VALUE exc, const char *fmt, ...) +{ + va_list args; + VALUE err; + va_start(args, fmt); + err = ossl_make_error(exc, fmt, args); + va_end(args); + rb_exc_raise(err); +} + +VALUE +ossl_exc_new(VALUE exc, const char *fmt, ...) +{ + va_list args; + VALUE err; + va_start(args, fmt); + err = ossl_make_error(exc, fmt, args); + va_end(args); + return err; } /* diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index e40c93b60..9ac152508 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -135,6 +135,7 @@ int ossl_pem_passwd_cb(char *, int, int, void *); */ #define OSSL_ErrMsg() ERR_reason_error_string(ERR_get_error()) NORETURN(void ossl_raise(VALUE, const char *, ...)); +VALUE ossl_exc_new(VALUE, const char *, ...); /* * Verify callback diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 767f81e54..61b30d3af 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -1114,15 +1114,17 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) rb_eof_error(); case SSL_ERROR_WANT_WRITE: if (nonblock) { - errno = EWOULDBLOCK; - rb_sys_fail("SSL_ERROR_WANT_WRITE"); + VALUE exc = ossl_exc_new(eSSLError, "write would block"); + rb_extend_object(exc, rb_mWaitWritable); + rb_exc_raise(exc); } rb_io_wait_writable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_WANT_READ: if (nonblock) { - errno = EWOULDBLOCK; - rb_sys_fail("SSL_ERROR_WANT_READ"); + VALUE exc = ossl_exc_new(eSSLError, "read would block"); + rb_extend_object(exc, rb_mWaitReadable); + rb_exc_raise(exc); } rb_io_wait_readable(FPTR_TO_FD(fptr)); continue; diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c index 34a5da317..b50959c84 100644 --- a/ext/socket/ancdata.c +++ b/ext/socket/ancdata.c @@ -1279,8 +1279,8 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock) } if (ss == -1) { - if (nonblock && errno == EWOULDBLOCK) - rb_sys_fail("sendmsg(2) WANT_WRITE"); + if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) + rb_mod_sys_fail(rb_mWaitWritable, "sendmsg(2) would block"); rb_sys_fail("sendmsg(2)"); } @@ -1564,8 +1564,8 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock) } if (ss == -1) { - if (nonblock && errno == EWOULDBLOCK) - rb_sys_fail("recvmsg(2) WANT_READ"); + if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) + rb_mod_sys_fail(rb_mWaitReadable, "recvmsg(2) would block"); #if defined(HAVE_ST_MSG_CONTROL) if (!gc_done && (errno == EMFILE || errno == EMSGSIZE)) { /* diff --git a/ext/socket/basicsocket.c b/ext/socket/basicsocket.c index a8ab3906e..78e675c0d 100644 --- a/ext/socket/basicsocket.c +++ b/ext/socket/basicsocket.c @@ -623,8 +623,12 @@ bsock_recv(int argc, VALUE *argv, VALUE sock) * c = TCPSocket.new(addr, port) * s = serv.accept * c.send "aaa", 0 - * IO.select([s]) # emulate blocking recv. - * p s.recv_nonblock(10) #=> "aaa" + * 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. @@ -632,6 +636,10 @@ bsock_recv(int argc, VALUE *argv, VALUE sock) * BasicSocket#recv_nonblock may raise any error corresponding to recvfrom(2) failure, * including Errno::EWOULDBLOCK. * + * If the exception is Errno::EWOULDBLOCK or Errno::AGAIN, + * it is extended by IO::WaitReadable. + * So IO::WaitReadable can be used to rescue the exceptions for retrying recv_nonblock. + * * === See * * Socket#recvfrom */ diff --git a/ext/socket/init.c b/ext/socket/init.c index 5830f5098..81237536c 100644 --- a/ext/socket/init.c +++ b/ext/socket/init.c @@ -200,7 +200,7 @@ rsock_s_recvfrom_nonblock(VALUE sock, int argc, VALUE *argv, enum sock_recv_type #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif - rb_sys_fail("recvfrom(2) WANT_READ"); + rb_mod_sys_fail(rb_mWaitReadable, "recvfrom(2) would block"); } rb_sys_fail("recvfrom(2)"); } @@ -470,7 +470,7 @@ rsock_s_accept_nonblock(VALUE klass, rb_io_t *fptr, struct sockaddr *sockaddr, s #if defined EPROTO case EPROTO: #endif - rb_sys_fail("accept(2) WANT_READ"); + rb_mod_sys_fail(rb_mWaitReadable, "accept(2) would block"); } rb_sys_fail("accept(2)"); } diff --git a/ext/socket/socket.c b/ext/socket/socket.c index 00f3525f1..8b3d681d0 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -280,7 +280,7 @@ sock_connect(VALUE sock, VALUE addr) * sockaddr = Socket.sockaddr_in(80, 'www.google.com') * begin # emulate blocking connect * socket.connect_nonblock(sockaddr) - * rescue Errno::EINPROGRESS + * rescue IO::WaitWritable * IO.select(nil, [socket]) # wait 3-way handshake completion * begin * socket.connect_nonblock(sockaddr) # check connection failure @@ -296,6 +296,10 @@ sock_connect(VALUE sock, VALUE addr) * 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. + * * === See * * Socket#connect */ @@ -312,7 +316,7 @@ sock_connect_nonblock(VALUE sock, VALUE addr) n = connect(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_LEN(addr)); if (n < 0) { if (errno == EINPROGRESS) - rb_sys_fail("connect(2) WANT_WRITE"); + rb_mod_sys_fail(rb_mWaitWritable, "connect(2) would block"); rb_sys_fail("connect(2)"); } @@ -638,7 +642,7 @@ sock_recvfrom(int argc, VALUE *argv, VALUE sock) * client, client_sockaddr = socket.accept * begin # emulate blocking recvfrom * pair = client.recvfrom_nonblock(20) - * rescue Errno::EAGAIN, Errno::EWOULDBLOCK + * rescue IO::WaitReadable * IO.select([client]) * retry * end @@ -662,6 +666,10 @@ sock_recvfrom(int argc, VALUE *argv, VALUE sock) * Socket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure, * including Errno::EWOULDBLOCK. * + * If the exception is Errno::EWOULDBLOCK or Errno::AGAIN, + * it is extended by IO::WaitReadable. + * So IO::WaitReadable can be used to rescue the exceptions for retrying recvfrom_nonblock. + * * === See * * Socket#recvfrom */ @@ -720,7 +728,7 @@ sock_accept(VALUE sock) * socket.listen(5) * begin # emulate blocking accept * client_socket, client_sockaddr = socket.accept_nonblock - * rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR + * rescue IO::WaitReadable, Errno::EINTR * IO.select([socket]) * retry * end @@ -743,6 +751,10 @@ sock_accept(VALUE sock) * * Socket#accept_nonblock may raise any error corresponding to accept(2) failure, * including Errno::EWOULDBLOCK. + * + * If the exception is Errno::EWOULDBLOCK, Errno::AGAIN, 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. * * === See * * Socket#accept diff --git a/ext/socket/tcpserver.c b/ext/socket/tcpserver.c index 1f03642f6..59cd7dd3c 100644 --- a/ext/socket/tcpserver.c +++ b/ext/socket/tcpserver.c @@ -69,7 +69,7 @@ tcp_accept(VALUE sock) * serv = TCPServer.new(2202) * begin # emulate blocking accept * sock = serv.accept_nonblock - * rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR + * rescue IO::WaitReadable, Errno::EINTR * IO.select([serv]) * retry * end @@ -80,6 +80,10 @@ tcp_accept(VALUE sock) * * TCPServer#accept_nonblock may raise any error corresponding to accept(2) failure, * including Errno::EWOULDBLOCK. + * + * If the exception is Errno::EWOULDBLOCK, Errno::AGAIN, Errno::ECONNABORTED, Errno::EPROTO, + * it is extended by IO::WaitReadable. + * So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock. * * === See * * TCPServer#accept diff --git a/ext/socket/udpsocket.c b/ext/socket/udpsocket.c index 2463c8325..ae1f70eb1 100644 --- a/ext/socket/udpsocket.c +++ b/ext/socket/udpsocket.c @@ -218,8 +218,12 @@ udp_send(int argc, VALUE *argv, VALUE sock) * s2.connect(*s1.addr.values_at(3,1)) * s1.connect(*s2.addr.values_at(3,1)) * s1.send "aaa", 0 - * IO.select([s2]) # emulate blocking recvfrom - * p s2.recvfrom_nonblock(10) #=> ["aaa", ["AF_INET", 33302, "localhost.localdomain", "127.0.0.1"]] + * 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. @@ -227,6 +231,10 @@ udp_send(int argc, VALUE *argv, VALUE sock) * UDPSocket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure, * including Errno::EWOULDBLOCK. * + * If the exception is Errno::EWOULDBLOCK or Errno::AGAIN, + * it is extended by IO::WaitReadable. + * So IO::WaitReadable can be used to rescue the exceptions for retrying recvfrom_nonblock. + * * === See * * Socket#recvfrom */ diff --git a/ext/socket/unixserver.c b/ext/socket/unixserver.c index ec8988d62..fa41cba02 100644 --- a/ext/socket/unixserver.c +++ b/ext/socket/unixserver.c @@ -70,7 +70,7 @@ unix_accept(VALUE sock) * serv = UNIXServer.new("/tmp/sock") * begin # emulate blocking accept * sock = serv.accept_nonblock - * rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR + * rescue IO::WaitReadable, Errno::EINTR * IO.select([serv]) * retry * end @@ -81,6 +81,10 @@ unix_accept(VALUE sock) * * UNIXServer#accept_nonblock may raise any error corresponding to accept(2) failure, * including Errno::EWOULDBLOCK. + * + * If the exception is Errno::EWOULDBLOCK, Errno::AGAIN, 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. * * === See * * UNIXServer#accept diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index ecffd6048..1e619da33 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -986,6 +986,7 @@ PRINTF_ARGS(NORETURN(void rb_raise(VALUE, const char*, ...)), 2, 3); PRINTF_ARGS(NORETURN(void rb_fatal(const char*, ...)), 1, 2); PRINTF_ARGS(NORETURN(void rb_bug(const char*, ...)), 1, 2); NORETURN(void rb_sys_fail(const char*)); +NORETURN(void rb_mod_sys_fail(VALUE, const char*)); NORETURN(void rb_iter_break(void)); NORETURN(void rb_exit(int)); NORETURN(void rb_notimplement(void)); @@ -1040,6 +1041,8 @@ RUBY_EXTERN VALUE rb_mFileTest; RUBY_EXTERN VALUE rb_mGC; RUBY_EXTERN VALUE rb_mMath; RUBY_EXTERN VALUE rb_mProcess; +RUBY_EXTERN VALUE rb_mWaitReadable; +RUBY_EXTERN VALUE rb_mWaitWritable; RUBY_EXTERN VALUE rb_cBasicObject; RUBY_EXTERN VALUE rb_cObject; diff --git a/io.c b/io.c index 0464e5b4c..47f036741 100644 --- a/io.c +++ b/io.c @@ -110,6 +110,8 @@ extern void Init_File(void); VALUE rb_cIO; VALUE rb_eEOFError; VALUE rb_eIOError; +VALUE rb_mWaitReadable; +VALUE rb_mWaitWritable; VALUE rb_stdin, rb_stdout, rb_stderr; VALUE rb_deferr; /* rescue VIM plugin */ @@ -1754,8 +1756,8 @@ io_getpartial(int argc, VALUE *argv, VALUE io, int nonblock) if (n < 0) { if (!nonblock && rb_io_wait_readable(fptr->fd)) goto again; - if (nonblock && errno == EWOULDBLOCK) - rb_sys_fail("WANT_READ"); + if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) + rb_mod_sys_fail(rb_mWaitReadable, "read would block"); rb_sys_fail_path(fptr->pathv); } else if (n == 0) { @@ -1855,20 +1857,24 @@ io_readpartial(int argc, VALUE *argv, VALUE io) * It causes all errors the read(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc. * The caller should care such errors. * + * If the exception is Errno::EWOULDBLOCK or Errno::AGAIN, + * it is extended by IO::WaitReadable. + * So IO::WaitReadable can be used to rescue the exceptions for retrying read_nonblock. + * * read_nonblock causes EOFError on EOF. * * If the read buffer is not empty, * read_nonblock reads from the buffer like readpartial. * In this case, the read(2) system call is not called. * - * When read_nonblock raises EWOULDBLOCK, + * When read_nonblock raises an exception kind of IO::WaitReadable, * read_nonblock should not be called * until io is readable for avoiding busy loop. * This can be done as follows. * * begin * result = io.read_nonblock(maxlen) - * rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR + * rescue IO::WaitReadable, Errno::EINTR * IO.select([io]) * retry * end @@ -1904,6 +1910,10 @@ io_read_nonblock(int argc, VALUE *argv, VALUE io) * The result may also be smaller than string.length (partial write). * The caller should care such errors and partial write. * + * If the exception is Errno::EWOULDBLOCK or Errno::AGAIN, + * it is extended by IO::WaitWritable. + * So IO::WaitWritable can be used to rescue the exceptions for retrying write_nonblock. + * * # Creates a pipe. * r, w = IO.pipe * @@ -1917,14 +1927,14 @@ io_read_nonblock(int argc, VALUE *argv, VALUE io) * * If the write buffer is not empty, it is flushed at first. * - * When write_nonblock raises EWOULDBLOCK, + * When write_nonblock raises an exception kind of IO::WaitWritable, * write_nonblock should not be called * until io is writable for avoiding busy loop. * This can be done as follows. * * begin * result = io.write_nonblock(string) - * rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR + * rescue IO::WaitWritable, Errno::EINTR * IO.select(nil, [io]) * retry * end @@ -1955,8 +1965,8 @@ rb_io_write_nonblock(VALUE io, VALUE str) n = write(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str)); if (n == -1) { - if (errno == EWOULDBLOCK) - rb_sys_fail("WANT_WRITE"); + if (errno == EWOULDBLOCK || errno == EAGAIN) + rb_mod_sys_fail(rb_mWaitWritable, "write would block"); rb_sys_fail_path(fptr->pathv); } @@ -8673,6 +8683,9 @@ Init_IO(void) rb_cIO = rb_define_class("IO", rb_cObject); rb_include_module(rb_cIO, rb_mEnumerable); + rb_mWaitReadable = rb_define_module_under(rb_cIO, "WaitReadable"); + rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable"); + #if 0 /* This is necessary only for forcing rdoc handle File::open */ rb_define_singleton_method(rb_cFile, "open", rb_io_s_open, -1); diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb index e2423f298..d927c52f3 100644 --- a/lib/net/protocol.rb +++ b/lib/net/protocol.rb @@ -133,12 +133,20 @@ module Net # :nodoc: def rbuf_fill begin @rbuf << @io.read_nonblock(BUFSIZE) - rescue Errno::EWOULDBLOCK + rescue IO::WaitReadable if IO.select([@io], nil, nil, @read_timeout) retry else raise Timeout::TimeoutError end + rescue IO::WaitWritable + # OpenSSL::Buffering#read_nonblock may fail with IO::WaitWritable. + # http://www.openssl.org/support/faq.html#PROG10 + if IO.select(nil, [@io], nil, @read_timeout) + retry + else + raise Timeout::TimeoutError + end end end diff --git a/test/openssl/test_pair.rb b/test/openssl/test_pair.rb index 9d22a87e2..e5beebc50 100644 --- a/test/openssl/test_pair.rb +++ b/test/openssl/test_pair.rb @@ -147,14 +147,14 @@ class OpenSSL::TestPair < Test::Unit::TestCase def test_read_nonblock ssl_pair {|s1, s2| err = nil - assert_raise(Errno::EWOULDBLOCK) { + assert_raise(OpenSSL::SSL::SSLError) { begin s2.read_nonblock(10) ensure err = $! end } - assert_match(/SSL_ERROR_WANT_READ/, err.message) + assert_kind_of(IO::WaitReadable, err) s1.write "abc\ndef\n" IO.select([s2]) assert_equal("ab", s2.read_nonblock(2)) diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 5053634a8..4688552cd 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -172,12 +172,12 @@ class OpenSSL::TestSSL < Test::Unit::TestCase ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.sync_close = true ssl.connect - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { ssl.read_nonblock(100) } + assert_raise(IO::WaitReadable) { ssl.read_nonblock(100) } ssl.write("abc\n") IO.select [ssl] assert_equal('a', ssl.read_nonblock(1)) assert_equal("bc\n", ssl.read_nonblock(100)) - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { ssl.read_nonblock(100) } + assert_raise(IO::WaitReadable) { ssl.read_nonblock(100) } } end diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 91da8c03c..f45677988 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -864,7 +864,7 @@ class TestIO < Test::Unit::TestCase begin r.read_nonblock 4096 rescue Errno::EWOULDBLOCK - assert_match(/WANT_READ/, $!.message) + assert_kind_of(IO::WaitReadable, $!) end } end @@ -877,7 +877,7 @@ class TestIO < Test::Unit::TestCase w.write_nonblock "a"*100000 } rescue Errno::EWOULDBLOCK - assert_match(/WANT_WRITE/, $!.message) + assert_kind_of(IO::WaitWritable, $!) end } end diff --git a/test/socket/test_addrinfo.rb b/test/socket/test_addrinfo.rb index 00ed7f4e1..856fbd130 100644 --- a/test/socket/test_addrinfo.rb +++ b/test/socket/test_addrinfo.rb @@ -236,7 +236,7 @@ class TestSocketAddrinfo < Test::Unit::TestCase c.connect(serv.local_address) begin ret = serv.accept_nonblock - rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR + rescue IO::WaitReadable, Errno::EINTR IO.select([serv]) retry end @@ -299,7 +299,7 @@ class TestSocketAddrinfo < Test::Unit::TestCase s2.send("test-socket-recvfrom", 0, s1.getsockname) begin data, ai = s1.recvfrom_nonblock(100) - rescue Errno::EWOULDBLOCK + rescue IO::WaitReadable IO.select([s1]) retry end diff --git a/test/socket/test_nonblock.rb b/test/socket/test_nonblock.rb index 4e7807fa9..6b65cc386 100644 --- a/test/socket/test_nonblock.rb +++ b/test/socket/test_nonblock.rb @@ -12,13 +12,13 @@ class TestSocketNonblock < Test::Unit::TestCase serv = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) serv.bind(Socket.sockaddr_in(0, "127.0.0.1")) serv.listen(5) - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { serv.accept_nonblock } + assert_raise(IO::WaitReadable) { serv.accept_nonblock } c = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) c.connect(serv.getsockname) begin s, sockaddr = serv.accept_nonblock - rescue Errno::EWOULDBLOCK - IO.select nil, [serv] + rescue IO::WaitReadable + IO.select [serv] s, sockaddr = serv.accept_nonblock end assert_equal(Socket.unpack_sockaddr_in(c.getsockname), Socket.unpack_sockaddr_in(sockaddr)) @@ -57,8 +57,8 @@ class TestSocketNonblock < Test::Unit::TestCase u1 = UDPSocket.new u2 = UDPSocket.new u1.bind("127.0.0.1", 0) - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { u1.recvfrom_nonblock(100) } - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINVAL) { u2.recvfrom_nonblock(100) } + assert_raise(IO::WaitReadable) { u1.recvfrom_nonblock(100) } + assert_raise(IO::WaitReadable, Errno::EINVAL) { u2.recvfrom_nonblock(100) } u2.send("aaa", 0, u1.getsockname) IO.select [u1] mesg, inet_addr = u1.recvfrom_nonblock(100) @@ -67,7 +67,7 @@ class TestSocketNonblock < Test::Unit::TestCase af, port, host, addr = inet_addr u2_port, u2_addr = Socket.unpack_sockaddr_in(u2.getsockname) assert_equal(u2_port, port) - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { u1.recvfrom_nonblock(100) } + assert_raise(IO::WaitReadable) { u1.recvfrom_nonblock(100) } u2.send("", 0, u1.getsockname) assert_nothing_raised("cygwin 1.5.19 has a problem to send an empty UDP packet. [ruby-dev:28915]") { timeout(1) { IO.select [u1] } @@ -83,13 +83,13 @@ class TestSocketNonblock < Test::Unit::TestCase u1 = UDPSocket.new u2 = UDPSocket.new u1.bind("127.0.0.1", 0) - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { u1.recv_nonblock(100) } - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINVAL) { u2.recv_nonblock(100) } + assert_raise(IO::WaitReadable) { u1.recv_nonblock(100) } + assert_raise(IO::WaitReadable, Errno::EINVAL) { u2.recv_nonblock(100) } u2.send("aaa", 0, u1.getsockname) IO.select [u1] mesg = u1.recv_nonblock(100) assert_equal("aaa", mesg) - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { u1.recv_nonblock(100) } + assert_raise(IO::WaitReadable) { u1.recv_nonblock(100) } u2.send("", 0, u1.getsockname) assert_nothing_raised("cygwin 1.5.19 has a problem to send an empty UDP packet. [ruby-dev:28915]") { timeout(1) { IO.select [u1] } @@ -105,8 +105,8 @@ class TestSocketNonblock < Test::Unit::TestCase s1 = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) s1.bind(Socket.sockaddr_in(0, "127.0.0.1")) s2 = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { s1.recvfrom_nonblock(100) } - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINVAL) { s2.recvfrom_nonblock(100) } + assert_raise(IO::WaitReadable) { s1.recvfrom_nonblock(100) } + assert_raise(IO::WaitReadable, Errno::EINVAL) { s2.recvfrom_nonblock(100) } s2.send("aaa", 0, s1.getsockname) IO.select [s1] mesg, sockaddr = s1.recvfrom_nonblock(100) @@ -140,13 +140,13 @@ class TestSocketNonblock < Test::Unit::TestCase def test_tcp_recv_nonblock c, s = tcp_pair - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { c.recv_nonblock(100) } - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { s.recv_nonblock(100) } + assert_raise(IO::WaitReadable) { c.recv_nonblock(100) } + assert_raise(IO::WaitReadable) { s.recv_nonblock(100) } c.write("abc") IO.select [s] assert_equal("a", s.recv_nonblock(1)) assert_equal("bc", s.recv_nonblock(100)) - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { s.recv_nonblock(100) } + assert_raise(IO::WaitReadable) { s.recv_nonblock(100) } ensure c.close if c s.close if s @@ -154,13 +154,13 @@ class TestSocketNonblock < Test::Unit::TestCase def test_read_nonblock c, s = tcp_pair - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { c.read_nonblock(100) } - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { s.read_nonblock(100) } + assert_raise(IO::WaitReadable) { c.read_nonblock(100) } + assert_raise(IO::WaitReadable) { s.read_nonblock(100) } c.write("abc") IO.select [s] assert_equal("a", s.read_nonblock(1)) assert_equal("bc", s.read_nonblock(100)) - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { s.read_nonblock(100) } + assert_raise(IO::WaitReadable) { s.read_nonblock(100) } ensure c.close if c s.close if s @@ -175,7 +175,7 @@ class TestSocketNonblock < Test::Unit::TestCase ret = c.write_nonblock(str) assert_operator(ret, :>, 0) loop { - assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { + assert_raise(IO::WaitWritable) { loop { ret = c.write_nonblock(str) assert_operator(ret, :>, 0) @@ -197,7 +197,7 @@ class TestSocketNonblock < Test::Unit::TestCase c.sendmsg_nonblock("a" * 100000) } rescue Errno::EWOULDBLOCK - assert_match(/WANT_WRITE/, $!.message) + assert_kind_of(IO::WaitWritable, $!) end } end @@ -207,7 +207,7 @@ class TestSocketNonblock < Test::Unit::TestCase begin c.recvmsg_nonblock(4096) rescue Errno::EWOULDBLOCK - assert_match(/WANT_READ/, $!.message) + assert_kind_of(IO::WaitReadable, $!) end } end @@ -217,7 +217,7 @@ class TestSocketNonblock < Test::Unit::TestCase begin c.recv_nonblock(4096) rescue Errno::EWOULDBLOCK - assert_match(/WANT_READ/, $!.message) + assert_kind_of(IO::WaitReadable, $!) end } end @@ -229,7 +229,7 @@ class TestSocketNonblock < Test::Unit::TestCase begin c.connect_nonblock(Socket.sockaddr_in(port, "127.0.0.1")) rescue Errno::EINPROGRESS - assert_match(/WANT_WRITE/, $!.message) + assert_kind_of(IO::WaitWritable, $!) end ensure serv.close if serv && !serv.closed? @@ -243,8 +243,8 @@ class TestSocketNonblock < Test::Unit::TestCase port = serv.local_address.ip_port begin s, _ = serv.accept_nonblock - rescue Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO - assert_match(/WANT_READ/, $!.message) + rescue Errno::EWOULDBLOCK + assert_kind_of(IO::WaitReadable, $!) end ensure serv.close if serv && !serv.closed? -- cgit