1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* 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: svn+ssh://ci.ruby-lang.org/ruby/trunk@23006 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
akr 2009-03-19 11:40:38 +00:00
parent b7800329d3
commit c4049f4cb6
20 changed files with 208 additions and 70 deletions

View file

@ -1,3 +1,45 @@
Thu Mar 19 20:29:40 2009 Tanaka Akira <akr@fsij.org>
* 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 <zn@mbf.nifty.com> Wed Mar 18 16:59:48 2009 Kazuhiro NISHIYAMA <zn@mbf.nifty.com>
* test/test_syslog.rb (TestSyslog#test_open): check * test/test_syslog.rb (TestSyslog#test_open): check

20
error.c
View file

@ -1131,8 +1131,8 @@ rb_fatal(const char *fmt, ...)
rb_exc_fatal(rb_exc_new3(rb_eFatal, mesg)); rb_exc_fatal(rb_exc_new3(rb_eFatal, mesg));
} }
void static VALUE
rb_sys_fail(const char *mesg) make_errno_exc(const char *mesg)
{ {
int n = errno; int n = errno;
VALUE arg; VALUE arg;
@ -1143,7 +1143,21 @@ rb_sys_fail(const char *mesg)
} }
arg = mesg ? rb_str_new2(mesg) : Qnil; 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 void

View file

@ -272,10 +272,9 @@ ossl_to_der_if_possible(VALUE obj)
/* /*
* Errors * Errors
*/ */
void static VALUE
ossl_raise(VALUE exc, const char *fmt, ...) ossl_make_error(VALUE exc, const char *fmt, va_list args)
{ {
va_list args;
char buf[BUFSIZ]; char buf[BUFSIZ];
const char *msg; const char *msg;
long e; long e;
@ -287,9 +286,7 @@ ossl_raise(VALUE exc, const char *fmt, ...)
e = ERR_peek_error(); e = ERR_peek_error();
#endif #endif
if (fmt) { if (fmt) {
va_start(args, fmt);
len = vsnprintf(buf, BUFSIZ, fmt, args); len = vsnprintf(buf, BUFSIZ, fmt, args);
va_end(args);
} }
if (len < BUFSIZ && e) { if (len < BUFSIZ && e) {
if (dOSSL == Qtrue) /* FULL INFO */ if (dOSSL == Qtrue) /* FULL INFO */
@ -306,7 +303,29 @@ ossl_raise(VALUE exc, const char *fmt, ...)
ERR_clear_error(); ERR_clear_error();
if(len > BUFSIZ) len = strlen(buf); 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;
} }
/* /*

View file

@ -135,6 +135,7 @@ int ossl_pem_passwd_cb(char *, int, int, void *);
*/ */
#define OSSL_ErrMsg() ERR_reason_error_string(ERR_get_error()) #define OSSL_ErrMsg() ERR_reason_error_string(ERR_get_error())
NORETURN(void ossl_raise(VALUE, const char *, ...)); NORETURN(void ossl_raise(VALUE, const char *, ...));
VALUE ossl_exc_new(VALUE, const char *, ...);
/* /*
* Verify callback * Verify callback

View file

@ -1114,15 +1114,17 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
rb_eof_error(); rb_eof_error();
case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_WRITE:
if (nonblock) { if (nonblock) {
errno = EWOULDBLOCK; VALUE exc = ossl_exc_new(eSSLError, "write would block");
rb_sys_fail("SSL_ERROR_WANT_WRITE"); rb_extend_object(exc, rb_mWaitWritable);
rb_exc_raise(exc);
} }
rb_io_wait_writable(FPTR_TO_FD(fptr)); rb_io_wait_writable(FPTR_TO_FD(fptr));
continue; continue;
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
if (nonblock) { if (nonblock) {
errno = EWOULDBLOCK; VALUE exc = ossl_exc_new(eSSLError, "read would block");
rb_sys_fail("SSL_ERROR_WANT_READ"); rb_extend_object(exc, rb_mWaitReadable);
rb_exc_raise(exc);
} }
rb_io_wait_readable(FPTR_TO_FD(fptr)); rb_io_wait_readable(FPTR_TO_FD(fptr));
continue; continue;

View file

@ -1279,8 +1279,8 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
} }
if (ss == -1) { if (ss == -1) {
if (nonblock && errno == EWOULDBLOCK) if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN))
rb_sys_fail("sendmsg(2) WANT_WRITE"); rb_mod_sys_fail(rb_mWaitWritable, "sendmsg(2) would block");
rb_sys_fail("sendmsg(2)"); rb_sys_fail("sendmsg(2)");
} }
@ -1564,8 +1564,8 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
} }
if (ss == -1) { if (ss == -1) {
if (nonblock && errno == EWOULDBLOCK) if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN))
rb_sys_fail("recvmsg(2) WANT_READ"); rb_mod_sys_fail(rb_mWaitReadable, "recvmsg(2) would block");
#if defined(HAVE_ST_MSG_CONTROL) #if defined(HAVE_ST_MSG_CONTROL)
if (!gc_done && (errno == EMFILE || errno == EMSGSIZE)) { if (!gc_done && (errno == EMFILE || errno == EMSGSIZE)) {
/* /*

View file

@ -623,8 +623,12 @@ bsock_recv(int argc, VALUE *argv, VALUE sock)
* c = TCPSocket.new(addr, port) * c = TCPSocket.new(addr, port)
* s = serv.accept * s = serv.accept
* c.send "aaa", 0 * c.send "aaa", 0
* IO.select([s]) # emulate blocking recv. * begin # emulate blocking recv.
* p s.recv_nonblock(10) #=> "aaa" * 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 * Refer to Socket#recvfrom for the exceptions that may be thrown if the call
* to _recv_nonblock_ fails. * 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, * BasicSocket#recv_nonblock may raise any error corresponding to recvfrom(2) failure,
* including Errno::EWOULDBLOCK. * 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 * === See
* * Socket#recvfrom * * Socket#recvfrom
*/ */

View file

@ -200,7 +200,7 @@ rsock_s_recvfrom_nonblock(VALUE sock, int argc, VALUE *argv, enum sock_recv_type
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK: case EWOULDBLOCK:
#endif #endif
rb_sys_fail("recvfrom(2) WANT_READ"); rb_mod_sys_fail(rb_mWaitReadable, "recvfrom(2) would block");
} }
rb_sys_fail("recvfrom(2)"); 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 #if defined EPROTO
case EPROTO: case EPROTO:
#endif #endif
rb_sys_fail("accept(2) WANT_READ"); rb_mod_sys_fail(rb_mWaitReadable, "accept(2) would block");
} }
rb_sys_fail("accept(2)"); rb_sys_fail("accept(2)");
} }

View file

@ -280,7 +280,7 @@ sock_connect(VALUE sock, VALUE addr)
* sockaddr = Socket.sockaddr_in(80, 'www.google.com') * sockaddr = Socket.sockaddr_in(80, 'www.google.com')
* begin # emulate blocking connect * begin # emulate blocking connect
* socket.connect_nonblock(sockaddr) * socket.connect_nonblock(sockaddr)
* rescue Errno::EINPROGRESS * rescue IO::WaitWritable
* IO.select(nil, [socket]) # wait 3-way handshake completion * IO.select(nil, [socket]) # wait 3-way handshake completion
* begin * begin
* socket.connect_nonblock(sockaddr) # check connection failure * 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, * Socket#connect_nonblock may raise any error corresponding to connect(2) failure,
* including Errno::EINPROGRESS. * 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 * === See
* * Socket#connect * * 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)); n = connect(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_LEN(addr));
if (n < 0) { if (n < 0) {
if (errno == EINPROGRESS) 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)"); rb_sys_fail("connect(2)");
} }
@ -638,7 +642,7 @@ sock_recvfrom(int argc, VALUE *argv, VALUE sock)
* client, client_sockaddr = socket.accept * client, client_sockaddr = socket.accept
* begin # emulate blocking recvfrom * begin # emulate blocking recvfrom
* pair = client.recvfrom_nonblock(20) * pair = client.recvfrom_nonblock(20)
* rescue Errno::EAGAIN, Errno::EWOULDBLOCK * rescue IO::WaitReadable
* IO.select([client]) * IO.select([client])
* retry * retry
* end * 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, * Socket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure,
* including Errno::EWOULDBLOCK. * 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 * === See
* * Socket#recvfrom * * Socket#recvfrom
*/ */
@ -720,7 +728,7 @@ sock_accept(VALUE sock)
* socket.listen(5) * socket.listen(5)
* begin # emulate blocking accept * begin # emulate blocking accept
* client_socket, client_sockaddr = socket.accept_nonblock * 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]) * IO.select([socket])
* retry * retry
* end * end
@ -744,6 +752,10 @@ sock_accept(VALUE sock)
* Socket#accept_nonblock may raise any error corresponding to accept(2) failure, * Socket#accept_nonblock may raise any error corresponding to accept(2) failure,
* including Errno::EWOULDBLOCK. * 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 * === See
* * Socket#accept * * Socket#accept
*/ */

View file

@ -69,7 +69,7 @@ tcp_accept(VALUE sock)
* serv = TCPServer.new(2202) * serv = TCPServer.new(2202)
* begin # emulate blocking accept * begin # emulate blocking accept
* sock = serv.accept_nonblock * sock = serv.accept_nonblock
* rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR * rescue IO::WaitReadable, Errno::EINTR
* IO.select([serv]) * IO.select([serv])
* retry * retry
* end * end
@ -81,6 +81,10 @@ tcp_accept(VALUE sock)
* TCPServer#accept_nonblock may raise any error corresponding to accept(2) failure, * TCPServer#accept_nonblock may raise any error corresponding to accept(2) failure,
* including Errno::EWOULDBLOCK. * 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 * === See
* * TCPServer#accept * * TCPServer#accept
* * Socket#accept * * Socket#accept

View file

@ -218,8 +218,12 @@ udp_send(int argc, VALUE *argv, VALUE sock)
* s2.connect(*s1.addr.values_at(3,1)) * s2.connect(*s1.addr.values_at(3,1))
* s1.connect(*s2.addr.values_at(3,1)) * s1.connect(*s2.addr.values_at(3,1))
* s1.send "aaa", 0 * s1.send "aaa", 0
* IO.select([s2]) # emulate blocking recvfrom * begin # emulate blocking recvfrom
* p s2.recvfrom_nonblock(10) #=> ["aaa", ["AF_INET", 33302, "localhost.localdomain", "127.0.0.1"]] * 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 * Refer to Socket#recvfrom for the exceptions that may be thrown if the call
* to _recvfrom_nonblock_ fails. * 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, * UDPSocket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure,
* including Errno::EWOULDBLOCK. * 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 * === See
* * Socket#recvfrom * * Socket#recvfrom
*/ */

View file

@ -70,7 +70,7 @@ unix_accept(VALUE sock)
* serv = UNIXServer.new("/tmp/sock") * serv = UNIXServer.new("/tmp/sock")
* begin # emulate blocking accept * begin # emulate blocking accept
* sock = serv.accept_nonblock * sock = serv.accept_nonblock
* rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR * rescue IO::WaitReadable, Errno::EINTR
* IO.select([serv]) * IO.select([serv])
* retry * retry
* end * end
@ -82,6 +82,10 @@ unix_accept(VALUE sock)
* UNIXServer#accept_nonblock may raise any error corresponding to accept(2) failure, * UNIXServer#accept_nonblock may raise any error corresponding to accept(2) failure,
* including Errno::EWOULDBLOCK. * 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 * === See
* * UNIXServer#accept * * UNIXServer#accept
* * Socket#accept * * Socket#accept

View file

@ -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_fatal(const char*, ...)), 1, 2);
PRINTF_ARGS(NORETURN(void rb_bug(const char*, ...)), 1, 2); PRINTF_ARGS(NORETURN(void rb_bug(const char*, ...)), 1, 2);
NORETURN(void rb_sys_fail(const char*)); 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_iter_break(void));
NORETURN(void rb_exit(int)); NORETURN(void rb_exit(int));
NORETURN(void rb_notimplement(void)); NORETURN(void rb_notimplement(void));
@ -1040,6 +1041,8 @@ RUBY_EXTERN VALUE rb_mFileTest;
RUBY_EXTERN VALUE rb_mGC; RUBY_EXTERN VALUE rb_mGC;
RUBY_EXTERN VALUE rb_mMath; RUBY_EXTERN VALUE rb_mMath;
RUBY_EXTERN VALUE rb_mProcess; RUBY_EXTERN VALUE rb_mProcess;
RUBY_EXTERN VALUE rb_mWaitReadable;
RUBY_EXTERN VALUE rb_mWaitWritable;
RUBY_EXTERN VALUE rb_cBasicObject; RUBY_EXTERN VALUE rb_cBasicObject;
RUBY_EXTERN VALUE rb_cObject; RUBY_EXTERN VALUE rb_cObject;

29
io.c
View file

@ -110,6 +110,8 @@ extern void Init_File(void);
VALUE rb_cIO; VALUE rb_cIO;
VALUE rb_eEOFError; VALUE rb_eEOFError;
VALUE rb_eIOError; VALUE rb_eIOError;
VALUE rb_mWaitReadable;
VALUE rb_mWaitWritable;
VALUE rb_stdin, rb_stdout, rb_stderr; VALUE rb_stdin, rb_stdout, rb_stderr;
VALUE rb_deferr; /* rescue VIM plugin */ VALUE rb_deferr; /* rescue VIM plugin */
@ -1754,8 +1756,8 @@ io_getpartial(int argc, VALUE *argv, VALUE io, int nonblock)
if (n < 0) { if (n < 0) {
if (!nonblock && rb_io_wait_readable(fptr->fd)) if (!nonblock && rb_io_wait_readable(fptr->fd))
goto again; goto again;
if (nonblock && errno == EWOULDBLOCK) if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN))
rb_sys_fail("WANT_READ"); rb_mod_sys_fail(rb_mWaitReadable, "read would block");
rb_sys_fail_path(fptr->pathv); rb_sys_fail_path(fptr->pathv);
} }
else if (n == 0) { 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. * It causes all errors the read(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc.
* The caller should care such errors. * 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. * read_nonblock causes EOFError on EOF.
* *
* If the read buffer is not empty, * If the read buffer is not empty,
* read_nonblock reads from the buffer like readpartial. * read_nonblock reads from the buffer like readpartial.
* In this case, the read(2) system call is not called. * 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 * read_nonblock should not be called
* until io is readable for avoiding busy loop. * until io is readable for avoiding busy loop.
* This can be done as follows. * This can be done as follows.
* *
* begin * begin
* result = io.read_nonblock(maxlen) * result = io.read_nonblock(maxlen)
* rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR * rescue IO::WaitReadable, Errno::EINTR
* IO.select([io]) * IO.select([io])
* retry * retry
* end * 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 result may also be smaller than string.length (partial write).
* The caller should care such errors and 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. * # Creates a pipe.
* r, w = IO.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. * 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 * write_nonblock should not be called
* until io is writable for avoiding busy loop. * until io is writable for avoiding busy loop.
* This can be done as follows. * This can be done as follows.
* *
* begin * begin
* result = io.write_nonblock(string) * result = io.write_nonblock(string)
* rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR * rescue IO::WaitWritable, Errno::EINTR
* IO.select(nil, [io]) * IO.select(nil, [io])
* retry * retry
* end * end
@ -1955,8 +1965,8 @@ rb_io_write_nonblock(VALUE io, VALUE str)
n = write(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str)); n = write(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str));
if (n == -1) { if (n == -1) {
if (errno == EWOULDBLOCK) if (errno == EWOULDBLOCK || errno == EAGAIN)
rb_sys_fail("WANT_WRITE"); rb_mod_sys_fail(rb_mWaitWritable, "write would block");
rb_sys_fail_path(fptr->pathv); rb_sys_fail_path(fptr->pathv);
} }
@ -8673,6 +8683,9 @@ Init_IO(void)
rb_cIO = rb_define_class("IO", rb_cObject); rb_cIO = rb_define_class("IO", rb_cObject);
rb_include_module(rb_cIO, rb_mEnumerable); 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 #if 0
/* This is necessary only for forcing rdoc handle File::open */ /* This is necessary only for forcing rdoc handle File::open */
rb_define_singleton_method(rb_cFile, "open", rb_io_s_open, -1); rb_define_singleton_method(rb_cFile, "open", rb_io_s_open, -1);

View file

@ -133,12 +133,20 @@ module Net # :nodoc:
def rbuf_fill def rbuf_fill
begin begin
@rbuf << @io.read_nonblock(BUFSIZE) @rbuf << @io.read_nonblock(BUFSIZE)
rescue Errno::EWOULDBLOCK rescue IO::WaitReadable
if IO.select([@io], nil, nil, @read_timeout) if IO.select([@io], nil, nil, @read_timeout)
retry retry
else else
raise Timeout::TimeoutError raise Timeout::TimeoutError
end 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
end end

View file

@ -147,14 +147,14 @@ class OpenSSL::TestPair < Test::Unit::TestCase
def test_read_nonblock def test_read_nonblock
ssl_pair {|s1, s2| ssl_pair {|s1, s2|
err = nil err = nil
assert_raise(Errno::EWOULDBLOCK) { assert_raise(OpenSSL::SSL::SSLError) {
begin begin
s2.read_nonblock(10) s2.read_nonblock(10)
ensure ensure
err = $! err = $!
end end
} }
assert_match(/SSL_ERROR_WANT_READ/, err.message) assert_kind_of(IO::WaitReadable, err)
s1.write "abc\ndef\n" s1.write "abc\ndef\n"
IO.select([s2]) IO.select([s2])
assert_equal("ab", s2.read_nonblock(2)) assert_equal("ab", s2.read_nonblock(2))

View file

@ -172,12 +172,12 @@ class OpenSSL::TestSSL < Test::Unit::TestCase
ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl = OpenSSL::SSL::SSLSocket.new(sock)
ssl.sync_close = true ssl.sync_close = true
ssl.connect ssl.connect
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { ssl.read_nonblock(100) } assert_raise(IO::WaitReadable) { ssl.read_nonblock(100) }
ssl.write("abc\n") ssl.write("abc\n")
IO.select [ssl] IO.select [ssl]
assert_equal('a', ssl.read_nonblock(1)) assert_equal('a', ssl.read_nonblock(1))
assert_equal("bc\n", ssl.read_nonblock(100)) 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 end

View file

@ -864,7 +864,7 @@ class TestIO < Test::Unit::TestCase
begin begin
r.read_nonblock 4096 r.read_nonblock 4096
rescue Errno::EWOULDBLOCK rescue Errno::EWOULDBLOCK
assert_match(/WANT_READ/, $!.message) assert_kind_of(IO::WaitReadable, $!)
end end
} }
end end
@ -877,7 +877,7 @@ class TestIO < Test::Unit::TestCase
w.write_nonblock "a"*100000 w.write_nonblock "a"*100000
} }
rescue Errno::EWOULDBLOCK rescue Errno::EWOULDBLOCK
assert_match(/WANT_WRITE/, $!.message) assert_kind_of(IO::WaitWritable, $!)
end end
} }
end end

View file

@ -236,7 +236,7 @@ class TestSocketAddrinfo < Test::Unit::TestCase
c.connect(serv.local_address) c.connect(serv.local_address)
begin begin
ret = serv.accept_nonblock ret = serv.accept_nonblock
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR rescue IO::WaitReadable, Errno::EINTR
IO.select([serv]) IO.select([serv])
retry retry
end end
@ -299,7 +299,7 @@ class TestSocketAddrinfo < Test::Unit::TestCase
s2.send("test-socket-recvfrom", 0, s1.getsockname) s2.send("test-socket-recvfrom", 0, s1.getsockname)
begin begin
data, ai = s1.recvfrom_nonblock(100) data, ai = s1.recvfrom_nonblock(100)
rescue Errno::EWOULDBLOCK rescue IO::WaitReadable
IO.select([s1]) IO.select([s1])
retry retry
end end

View file

@ -12,13 +12,13 @@ class TestSocketNonblock < Test::Unit::TestCase
serv = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) serv = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
serv.bind(Socket.sockaddr_in(0, "127.0.0.1")) serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
serv.listen(5) 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 = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
c.connect(serv.getsockname) c.connect(serv.getsockname)
begin begin
s, sockaddr = serv.accept_nonblock s, sockaddr = serv.accept_nonblock
rescue Errno::EWOULDBLOCK rescue IO::WaitReadable
IO.select nil, [serv] IO.select [serv]
s, sockaddr = serv.accept_nonblock s, sockaddr = serv.accept_nonblock
end end
assert_equal(Socket.unpack_sockaddr_in(c.getsockname), Socket.unpack_sockaddr_in(sockaddr)) 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 u1 = UDPSocket.new
u2 = UDPSocket.new u2 = UDPSocket.new
u1.bind("127.0.0.1", 0) u1.bind("127.0.0.1", 0)
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { u1.recvfrom_nonblock(100) } assert_raise(IO::WaitReadable) { u1.recvfrom_nonblock(100) }
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINVAL) { u2.recvfrom_nonblock(100) } assert_raise(IO::WaitReadable, Errno::EINVAL) { u2.recvfrom_nonblock(100) }
u2.send("aaa", 0, u1.getsockname) u2.send("aaa", 0, u1.getsockname)
IO.select [u1] IO.select [u1]
mesg, inet_addr = u1.recvfrom_nonblock(100) mesg, inet_addr = u1.recvfrom_nonblock(100)
@ -67,7 +67,7 @@ class TestSocketNonblock < Test::Unit::TestCase
af, port, host, addr = inet_addr af, port, host, addr = inet_addr
u2_port, u2_addr = Socket.unpack_sockaddr_in(u2.getsockname) u2_port, u2_addr = Socket.unpack_sockaddr_in(u2.getsockname)
assert_equal(u2_port, port) 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) u2.send("", 0, u1.getsockname)
assert_nothing_raised("cygwin 1.5.19 has a problem to send an empty UDP packet. [ruby-dev:28915]") { assert_nothing_raised("cygwin 1.5.19 has a problem to send an empty UDP packet. [ruby-dev:28915]") {
timeout(1) { IO.select [u1] } timeout(1) { IO.select [u1] }
@ -83,13 +83,13 @@ class TestSocketNonblock < Test::Unit::TestCase
u1 = UDPSocket.new u1 = UDPSocket.new
u2 = UDPSocket.new u2 = UDPSocket.new
u1.bind("127.0.0.1", 0) u1.bind("127.0.0.1", 0)
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { u1.recv_nonblock(100) } assert_raise(IO::WaitReadable) { u1.recv_nonblock(100) }
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINVAL) { u2.recv_nonblock(100) } assert_raise(IO::WaitReadable, Errno::EINVAL) { u2.recv_nonblock(100) }
u2.send("aaa", 0, u1.getsockname) u2.send("aaa", 0, u1.getsockname)
IO.select [u1] IO.select [u1]
mesg = u1.recv_nonblock(100) mesg = u1.recv_nonblock(100)
assert_equal("aaa", mesg) 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) u2.send("", 0, u1.getsockname)
assert_nothing_raised("cygwin 1.5.19 has a problem to send an empty UDP packet. [ruby-dev:28915]") { assert_nothing_raised("cygwin 1.5.19 has a problem to send an empty UDP packet. [ruby-dev:28915]") {
timeout(1) { IO.select [u1] } 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 = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
s1.bind(Socket.sockaddr_in(0, "127.0.0.1")) s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
s2 = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) s2 = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { s1.recvfrom_nonblock(100) } assert_raise(IO::WaitReadable) { s1.recvfrom_nonblock(100) }
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINVAL) { s2.recvfrom_nonblock(100) } assert_raise(IO::WaitReadable, Errno::EINVAL) { s2.recvfrom_nonblock(100) }
s2.send("aaa", 0, s1.getsockname) s2.send("aaa", 0, s1.getsockname)
IO.select [s1] IO.select [s1]
mesg, sockaddr = s1.recvfrom_nonblock(100) mesg, sockaddr = s1.recvfrom_nonblock(100)
@ -140,13 +140,13 @@ class TestSocketNonblock < Test::Unit::TestCase
def test_tcp_recv_nonblock def test_tcp_recv_nonblock
c, s = tcp_pair c, s = tcp_pair
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { c.recv_nonblock(100) } assert_raise(IO::WaitReadable) { c.recv_nonblock(100) }
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { s.recv_nonblock(100) } assert_raise(IO::WaitReadable) { s.recv_nonblock(100) }
c.write("abc") c.write("abc")
IO.select [s] IO.select [s]
assert_equal("a", s.recv_nonblock(1)) assert_equal("a", s.recv_nonblock(1))
assert_equal("bc", s.recv_nonblock(100)) 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 ensure
c.close if c c.close if c
s.close if s s.close if s
@ -154,13 +154,13 @@ class TestSocketNonblock < Test::Unit::TestCase
def test_read_nonblock def test_read_nonblock
c, s = tcp_pair c, s = tcp_pair
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { c.read_nonblock(100) } assert_raise(IO::WaitReadable) { c.read_nonblock(100) }
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { s.read_nonblock(100) } assert_raise(IO::WaitReadable) { s.read_nonblock(100) }
c.write("abc") c.write("abc")
IO.select [s] IO.select [s]
assert_equal("a", s.read_nonblock(1)) assert_equal("a", s.read_nonblock(1))
assert_equal("bc", s.read_nonblock(100)) 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 ensure
c.close if c c.close if c
s.close if s s.close if s
@ -175,7 +175,7 @@ class TestSocketNonblock < Test::Unit::TestCase
ret = c.write_nonblock(str) ret = c.write_nonblock(str)
assert_operator(ret, :>, 0) assert_operator(ret, :>, 0)
loop { loop {
assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { assert_raise(IO::WaitWritable) {
loop { loop {
ret = c.write_nonblock(str) ret = c.write_nonblock(str)
assert_operator(ret, :>, 0) assert_operator(ret, :>, 0)
@ -197,7 +197,7 @@ class TestSocketNonblock < Test::Unit::TestCase
c.sendmsg_nonblock("a" * 100000) c.sendmsg_nonblock("a" * 100000)
} }
rescue Errno::EWOULDBLOCK rescue Errno::EWOULDBLOCK
assert_match(/WANT_WRITE/, $!.message) assert_kind_of(IO::WaitWritable, $!)
end end
} }
end end
@ -207,7 +207,7 @@ class TestSocketNonblock < Test::Unit::TestCase
begin begin
c.recvmsg_nonblock(4096) c.recvmsg_nonblock(4096)
rescue Errno::EWOULDBLOCK rescue Errno::EWOULDBLOCK
assert_match(/WANT_READ/, $!.message) assert_kind_of(IO::WaitReadable, $!)
end end
} }
end end
@ -217,7 +217,7 @@ class TestSocketNonblock < Test::Unit::TestCase
begin begin
c.recv_nonblock(4096) c.recv_nonblock(4096)
rescue Errno::EWOULDBLOCK rescue Errno::EWOULDBLOCK
assert_match(/WANT_READ/, $!.message) assert_kind_of(IO::WaitReadable, $!)
end end
} }
end end
@ -229,7 +229,7 @@ class TestSocketNonblock < Test::Unit::TestCase
begin begin
c.connect_nonblock(Socket.sockaddr_in(port, "127.0.0.1")) c.connect_nonblock(Socket.sockaddr_in(port, "127.0.0.1"))
rescue Errno::EINPROGRESS rescue Errno::EINPROGRESS
assert_match(/WANT_WRITE/, $!.message) assert_kind_of(IO::WaitWritable, $!)
end end
ensure ensure
serv.close if serv && !serv.closed? serv.close if serv && !serv.closed?
@ -243,8 +243,8 @@ class TestSocketNonblock < Test::Unit::TestCase
port = serv.local_address.ip_port port = serv.local_address.ip_port
begin begin
s, _ = serv.accept_nonblock s, _ = serv.accept_nonblock
rescue Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO rescue Errno::EWOULDBLOCK
assert_match(/WANT_READ/, $!.message) assert_kind_of(IO::WaitReadable, $!)
end end
ensure ensure
serv.close if serv && !serv.closed? serv.close if serv && !serv.closed?