Index: include/ruby/ruby.h =================================================================== --- include/ruby/ruby.h (revision 40073) +++ include/ruby/ruby.h (working copy) @@ -1356,6 +1356,7 @@ NORETURN(void rb_sys_fail_str(VALUE)); NORETURN(void rb_mod_sys_fail(VALUE, const char*)); NORETURN(void rb_mod_sys_fail_str(VALUE, VALUE)); +NORETURN(void rb_readwrite_sys_fail(int, const char*)); NORETURN(void rb_iter_break(void)); NORETURN(void rb_iter_break_value(VALUE)); NORETURN(void rb_exit(int)); Index: io.c =================================================================== --- io.c (revision 40073) +++ io.c (working copy) @@ -133,6 +133,15 @@ VALUE rb_eIOError; VALUE rb_mWaitReadable; VALUE rb_mWaitWritable; +extern VALUE rb_eEAGAIN; +extern VALUE rb_eEWOULDBLOCK; +extern VALUE rb_eEINPROGRESS; +VALUE rb_eEAGAINWaitReadable; +VALUE rb_eEAGAINWaitWritable; +VALUE rb_eEWOULDBLOCKWaitReadable; +VALUE rb_eEWOULDBLOCKWaitWritable; +VALUE rb_eEINPROGRESSWaitWritable; +VALUE rb_eEINPROGRESSWaitReadable; VALUE rb_stdin, rb_stdout, rb_stderr; VALUE rb_deferr; /* rescue VIM plugin */ @@ -2342,6 +2351,9 @@ } } +void +rb_readwrite_sys_fail(int writable, const char *mesg); + static VALUE io_getpartial(int argc, VALUE *argv, VALUE io, int nonblock) { @@ -2380,7 +2392,7 @@ if (!nonblock && rb_io_wait_readable(fptr->fd)) goto again; if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) - rb_mod_sys_fail(rb_mWaitReadable, "read would block"); + rb_readwrite_sys_fail(0, "read would block"); rb_sys_fail_path(fptr->pathv); } } @@ -2599,7 +2611,7 @@ if (n == -1) { if (errno == EWOULDBLOCK || errno == EAGAIN) - rb_mod_sys_fail(rb_mWaitWritable, "write would block"); + rb_readwrite_sys_fail(1, "write would block"); rb_sys_fail_path(fptr->pathv); } @@ -11437,6 +11449,48 @@ return rb_io_write(argf_write_io(argf), str); } +void +rb_readwrite_sys_fail(int writable, const char *mesg) +{ + VALUE arg; + int n = errno; + arg = mesg ? rb_str_new2(mesg) : Qnil; + if (writable) { + switch (n) { + case EAGAIN: + rb_exc_raise(rb_class_new_instance(1, &arg, rb_eEAGAINWaitWritable)); + break; +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: + rb_exc_raise(rb_class_new_instance(1, &arg, rb_eEWOULDBLOCKWaitWritable)); + break; +#endif + case EINPROGRESS: + rb_exc_raise(rb_class_new_instance(1, &arg, rb_eEINPROGRESSWaitWritable)); + break; + default: + rb_mod_sys_fail_str(rb_mWaitWritable, arg); + } + } + else { + switch (n) { + case EAGAIN: + rb_exc_raise(rb_class_new_instance(1, &arg, rb_eEAGAINWaitReadable)); + break; +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: + rb_exc_raise(rb_class_new_instance(1, &arg, rb_eEWOULDBLOCKWaitReadable)); + break; +#endif + case EINPROGRESS: + rb_exc_raise(rb_class_new_instance(1, &arg, rb_eEINPROGRESSWaitReadable)); + break; + default: + rb_mod_sys_fail_str(rb_mWaitReadable, arg); + } + } +} + /* * Document-class: IOError * @@ -11645,6 +11699,25 @@ rb_mWaitReadable = rb_define_module_under(rb_cIO, "WaitReadable"); rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable"); + rb_eEAGAINWaitReadable = rb_define_class_under(rb_cIO, "EAGAINWaitReadable", rb_eEAGAIN); + rb_include_module(rb_eEAGAINWaitReadable, rb_mWaitReadable); + rb_eEAGAINWaitWritable = rb_define_class_under(rb_cIO, "EAGAINWaitWritable", rb_eEAGAIN); + rb_include_module(rb_eEAGAINWaitWritable, rb_mWaitWritable); + if (EAGAIN == EWOULDBLOCK) { + rb_eEWOULDBLOCKWaitReadable = rb_eEAGAINWaitReadable; + rb_define_const(rb_cIO, "EWOULDBLOCKWaitReadable", rb_eEAGAINWaitReadable); + rb_eEWOULDBLOCKWaitWritable = rb_eEAGAINWaitWritable; + rb_define_const(rb_cIO, "EWOULDBLOCKWaitWritable", rb_eEAGAINWaitWritable); + } else { + rb_eEWOULDBLOCKWaitReadable = rb_define_class_under(rb_cIO, "EWOULDBLOCKRWaiteadable", rb_eEWOULDBLOCK); + rb_include_module(rb_eEWOULDBLOCKWaitReadable, rb_mWaitReadable); + rb_eEWOULDBLOCKWaitWritable = rb_define_class_under(rb_cIO, "EWOULDBLOCKWaitWritable", rb_eEWOULDBLOCK); + rb_include_module(rb_eEWOULDBLOCKWaitWritable, rb_mWaitWritable); + } + rb_eEINPROGRESSWaitReadable = rb_define_class_under(rb_cIO, "EINPROGRESSWaitReadable", rb_eEINPROGRESS); + rb_include_module(rb_eEINPROGRESSWaitReadable, rb_mWaitReadable); + rb_eEINPROGRESSWaitWritable = rb_define_class_under(rb_cIO, "EINPROGRESSWaitWritable", rb_eEINPROGRESS); + rb_include_module(rb_eEINPROGRESSWaitWritable, rb_mWaitWritable); #if 0 /* This is necessary only for forcing rdoc handle File::open */ Index: ext/openssl/ossl_ssl.c =================================================================== --- ext/openssl/ossl_ssl.c (revision 40073) +++ ext/openssl/ossl_ssl.c (working copy) @@ -26,6 +26,8 @@ VALUE mSSL; VALUE eSSLError; +VALUE eSSLErrorWaitReadable; +VALUE eSSLErrorWaitWritable; VALUE cSSLContext; VALUE cSSLSocket; @@ -1230,8 +1232,7 @@ write_would_block(int nonblock) { if (nonblock) { - VALUE exc = ossl_exc_new(eSSLError, "write would block"); - rb_extend_object(exc, rb_mWaitWritable); + VALUE exc = ossl_exc_new(eSSLErrorWaitReadable, "write would block"); rb_exc_raise(exc); } } @@ -1240,8 +1241,7 @@ read_would_block(int nonblock) { if (nonblock) { - VALUE exc = ossl_exc_new(eSSLError, "read would block"); - rb_extend_object(exc, rb_mWaitReadable); + VALUE exc = ossl_exc_new(eSSLErrorWaitReadable, "read would block"); rb_exc_raise(exc); } } @@ -1846,6 +1846,10 @@ * Generic error class raised by SSLSocket and SSLContext. */ eSSLError = rb_define_class_under(mSSL, "SSLError", eOSSLError); + eSSLErrorWaitReadable = rb_define_class_under(mSSL, "SSLErrorWaitReadable", eSSLError); + rb_include_module(eSSLErrorWaitReadable, rb_mWaitReadable); + eSSLErrorWaitWritable = rb_define_class_under(mSSL, "SSLErrorWaitWritable", eSSLError); + rb_include_module(eSSLErrorWaitWritable, rb_mWaitWritable); Init_ossl_ssl_session(); Index: ext/socket/init.c =================================================================== --- ext/socket/init.c (revision 40073) +++ ext/socket/init.c (working copy) @@ -222,7 +222,7 @@ #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif - rb_mod_sys_fail(rb_mWaitReadable, "recvfrom(2) would block"); + rb_readwrite_sys_fail(0, "recvfrom(2) would block"); } rb_sys_fail("recvfrom(2)"); } @@ -537,7 +537,7 @@ #if defined EPROTO case EPROTO: #endif - rb_mod_sys_fail(rb_mWaitReadable, "accept(2) would block"); + rb_readwrite_sys_fail(0, "accept(2) would block"); } rb_sys_fail("accept(2)"); } Index: ext/socket/socket.c =================================================================== --- ext/socket/socket.c (revision 40073) +++ ext/socket/socket.c (working copy) @@ -374,7 +374,7 @@ n = connect(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_LENINT(addr)); if (n < 0) { if (errno == EINPROGRESS) - rb_mod_sys_fail(rb_mWaitWritable, "connect(2) would block"); + rb_readwrite_sys_fail(1, "connect(2) would block"); rb_sys_fail("connect(2)"); } Index: ext/socket/ancdata.c =================================================================== --- ext/socket/ancdata.c (revision 40073) +++ ext/socket/ancdata.c (working copy) @@ -1285,7 +1285,7 @@ if (ss == -1) { if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) - rb_mod_sys_fail(rb_mWaitWritable, "sendmsg(2) would block"); + rb_readwrite_sys_fail(1, "sendmsg(2) would block"); rb_sys_fail("sendmsg(2)"); } @@ -1600,7 +1600,7 @@ if (ss == -1) { if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) - rb_mod_sys_fail(rb_mWaitReadable, "recvmsg(2) would block"); + rb_readwrite_sys_fail(0, "recvmsg(2) would block"); #if defined(HAVE_ST_MSG_CONTROL) if (!gc_done && (errno == EMFILE || errno == EMSGSIZE)) { /* Index: error.c =================================================================== --- error.c (revision 40073) +++ error.c (working copy) @@ -39,6 +39,10 @@ #define WEXITSTATUS(status) (status) #endif +VALUE rb_eEAGAIN; +VALUE rb_eEWOULDBLOCK; +VALUE rb_eEINPROGRESS; + extern const char ruby_description[]; #define REPORTBUG_MSG \ @@ -1183,6 +1187,24 @@ if (!st_lookup(syserr_tbl, n, &error)) { error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError); + + /* capture nonblock errnos for WaitReadable/WaitWritable subclasses */ + switch (n) { + case EAGAIN: + rb_eEAGAIN = error; + +#if EAGAIN != EWOULDBLOCK + break; + case EWOULDBLOCK: +#endif + + rb_eEWOULDBLOCK = error; + break; + case EINPROGRESS: + rb_eEINPROGRESS = error; + break; + } + rb_define_const(error, "Errno", INT2NUM(n)); st_add_direct(syserr_tbl, n, error); } Index: test/openssl/test_pair.rb =================================================================== --- test/openssl/test_pair.rb (revision 40073) +++ test/openssl/test_pair.rb (working copy) @@ -141,7 +141,7 @@ def test_read_nonblock ssl_pair {|s1, s2| err = nil - assert_raise(OpenSSL::SSL::SSLError) { + assert_raise(OpenSSL::SSL::SSLErrorWaitReadable) { begin s2.read_nonblock(10) ensure Index: test/socket/test_unix.rb =================================================================== --- test/socket/test_unix.rb (revision 40073) +++ test/socket/test_unix.rb (working copy) @@ -348,7 +348,13 @@ def test_dgram_pair s1, s2 = UNIXSocket.pair(Socket::SOCK_DGRAM) - assert_raise(Errno::EAGAIN) { s1.recv_nonblock(10) } + begin + s1.recv_nonblock(10) + fail + rescue => e + assert(IO::EAGAINWaitReadable === e) + assert(IO::WaitReadable === e) + end s2.send("", 0) s2.send("haha", 0) s2.send("", 0) @@ -357,7 +363,7 @@ assert_equal("haha", s1.recv(10)) assert_equal("", s1.recv(10)) assert_equal("", s1.recv(10)) - assert_raise(Errno::EAGAIN) { s1.recv_nonblock(10) } + assert_raise(IO::EAGAINWaitReadable) { s1.recv_nonblock(10) } ensure s1.close if s1 s2.close if s2