diff --git a/error.c b/error.c index 0562de5..8304000 100644 --- a/error.c +++ b/error.c @@ -37,6 +37,9 @@ #define WEXITSTATUS(status) (status) #endif +VALUE rb_eEAGAIN; +VALUE rb_eEWOULDBLOCK; + extern const char ruby_description[]; #define REPORTBUG_MSG \ @@ -1148,6 +1151,15 @@ set_syserr(int n, const char *name) if (!st_lookup(syserr_tbl, n, &error)) { error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError); + + if (n == EAGAIN) { + rb_eEAGAIN = error; + if (n == EWOULDBLOCK) rb_eEWOULDBLOCK = error; /* same value */ + } + else if (n == EWOULDBLOCK) { /* different error */ + rb_eEWOULDBLOCK = error; + } + rb_define_const(error, "Errno", INT2NUM(n)); st_add_direct(syserr_tbl, n, error); } diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c index f8379cc..6d1f319 100644 --- a/ext/socket/ancdata.c +++ b/ext/socket/ancdata.c @@ -1284,7 +1284,7 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock) 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)"); } @@ -1593,7 +1593,7 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock) 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)) { /* diff --git a/ext/socket/init.c b/ext/socket/init.c index 990f1a4..933cb96 100644 --- a/ext/socket/init.c +++ b/ext/socket/init.c @@ -212,7 +212,7 @@ rsock_s_recvfrom_nonblock(VALUE sock, int argc, VALUE *argv, enum sock_recv_type #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)"); } @@ -525,7 +525,7 @@ rsock_s_accept_nonblock(VALUE klass, rb_io_t *fptr, struct sockaddr *sockaddr, s #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)"); } diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index 6909bf3..dbbfac9 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -1189,6 +1189,7 @@ NORETURN(void rb_sys_fail(const char*)); 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)); diff --git a/io.c b/io.c index 3fb9f1a..67effc9 100644 --- a/io.c +++ b/io.c @@ -120,6 +120,12 @@ VALUE rb_eEOFError; VALUE rb_eIOError; VALUE rb_mWaitReadable; VALUE rb_mWaitWritable; +extern VALUE rb_eEAGAIN; +extern VALUE rb_eEWOULDBLOCK; +VALUE rb_eEAGAINReadable; +VALUE rb_eEAGAINWritable; +VALUE rb_eEWOULDBLOCKReadable; +VALUE rb_eEWOULDBLOCKWritable; VALUE rb_stdin, rb_stdout, rb_stderr; VALUE rb_deferr; /* rescue VIM plugin */ @@ -2221,7 +2227,7 @@ io_getpartial(int argc, VALUE *argv, VALUE io, int nonblock) 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); } } @@ -2438,7 +2444,7 @@ rb_io_write_nonblock(VALUE io, VALUE str) 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); } @@ -11071,6 +11077,36 @@ argf_write(VALUE argf, VALUE str) 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) { + if (n == EAGAIN) { + rb_exc_raise(rb_class_new_instance(1, &arg, rb_eEAGAINWritable)); + } + else if (n == EWOULDBLOCK) { + rb_exc_raise(rb_class_new_instance(1, &arg, rb_eEWOULDBLOCKWritable)); + } + else { + rb_mod_sys_fail_str(rb_mWaitWritable, arg); + } + } + else { + if (n == EAGAIN) { + rb_exc_raise(rb_class_new_instance(1, &arg, rb_eEAGAINReadable)); + } + else if (n == EWOULDBLOCK) { + rb_exc_raise(rb_class_new_instance(1, &arg, rb_eEWOULDBLOCKReadable)); + } + else { + rb_mod_sys_fail_str(rb_mWaitReadable, arg); + } + } +} + /* * Document-class: IOError * @@ -11311,6 +11347,22 @@ Init_IO(void) rb_mWaitReadable = rb_define_module_under(rb_cIO, "WaitReadable"); rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable"); + rb_eEAGAINReadable = rb_define_class_under(rb_cIO, "EAGAINReadable", rb_eEAGAIN); + rb_include_module(rb_eEAGAINReadable, rb_mWaitReadable); + rb_eEAGAINWritable = rb_define_class_under(rb_cIO, "EAGAINWritable", rb_eEAGAIN); + rb_include_module(rb_eEAGAINWritable, rb_mWaitWritable); + if (EAGAIN == EWOULDBLOCK) { + rb_eEWOULDBLOCKReadable = rb_eEAGAINReadable; + rb_define_const(rb_cIO, "EWOULDBLOCKReadable", rb_eEAGAINReadable); + rb_eEWOULDBLOCKWritable = rb_eEAGAINWritable; + rb_define_const(rb_cIO, "EWOULDBLOCKWritable", rb_eEAGAINWritable); + } + else { + rb_eEWOULDBLOCKReadable = rb_define_class_under(rb_cIO, "EWOULDBLOCKReadable", rb_eEWOULDBLOCK); + rb_include_module(rb_eEWOULDBLOCKReadable, rb_mWaitReadable); + rb_eEWOULDBLOCKWritable = rb_define_class_under(rb_cIO, "EWOULDBLOCKWritable", rb_eEWOULDBLOCK); + rb_include_module(rb_eEWOULDBLOCKWritable, rb_mWaitWritable); + } #if 0 /* This is necessary only for forcing rdoc handle File::open */ diff --git a/test/socket/test_unix.rb b/test/socket/test_unix.rb index a9918b1..a108b99 100644 --- a/test/socket/test_unix.rb +++ b/test/socket/test_unix.rb @@ -345,7 +345,13 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase 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(Errno::EAGAIN === e) + assert(IO::WaitReadable === e) + end s2.send("", 0) s2.send("haha", 0) s2.send("", 0)