diff --git a/ChangeLog b/ChangeLog index 44894cd50d..b9a03fca0b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +Tue Apr 9 04:57:59 JST 2013 Charles Oliver Nutter + + * error.c: Capture EGAIN, EWOULDBLOCK, EINPROGRESS exceptions and + export them for use in WaitReadable/Writable exceptions. + * io.c: Create versions of EAGAIN, EWOULDBLOCK, EINPROGRESS that + include WaitReadable and WaitWritable. Add rb_readwrite_sys_fail + for nonblocking failures using those exceptions. Use that + function in io_getpartial and io_write_nonblock instead of + rb_mod_sys_fail + * ext/openssl/ossl_ssl.c: Add new SSLError subclasses that include + WaitReadable and WaitWritable. Use those classes for + write_would_block and read_would_block instead of rb_mod_sys_fail. + * ext/socket/ancdata.c: Use rb_readwrite_sys_fail instead of + rb_mod_sys_fail in bsock_sendmsg_internal and + bsock_recvmsg_internal. + * ext/socket/init.c: Use rb_readwrite_sys_fail instead of + rb_mod_sys_fail in rsock_s_recvfrom_nonblock and + rsock_s_connect_nonblock. + * ext/socket/socket.c: Use rb_readwrite_sys_fail instead of + rb_mod_sys_fail in sock_connect_nonblock. + * include/ruby/ruby.h: Export rb_readwrite_sys_fail for use instead + of rb_mod_sys_fail. Introduce new constants RB_IO_WAIT_READABLE and + RB_IO_WAIT_WRITABLE for first arg to rb_readwrite_sys_fail. + Tue Apr 9 02:44:32 2013 Nobuyoshi Nakada * ext/socket/extconf.rb: $defs needs -D or -U. nothing is added diff --git a/error.c b/error.c index 57784b6077..0261ec9388 100644 --- a/error.c +++ b/error.c @@ -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 @@ set_syserr(int n, const char *name) 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); } diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index d5c07d79a9..2ca8ae4529 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -29,6 +29,9 @@ VALUE eSSLError; VALUE cSSLContext; VALUE cSSLSocket; +static VALUE eSSLErrorWaitReadable; +static VALUE eSSLErrorWaitWritable; + #define ossl_sslctx_set_cert(o,v) rb_iv_set((o),"@cert",(v)) #define ossl_sslctx_set_key(o,v) rb_iv_set((o),"@key",(v)) #define ossl_sslctx_set_client_ca(o,v) rb_iv_set((o),"@client_ca",(v)) @@ -1230,8 +1233,7 @@ static void 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 +1242,7 @@ static void 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 +1847,10 @@ Init_ossl_ssl() * 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(); diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c index 5ca5bdd3bc..667dd4e95c 100644 --- a/ext/socket/ancdata.c +++ b/ext/socket/ancdata.c @@ -1285,7 +1285,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(RB_IO_WAIT_WRITABLE, "sendmsg(2) would block"); rb_sys_fail("sendmsg(2)"); } @@ -1600,7 +1600,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(RB_IO_WAIT_READABLE, "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 3fda607bee..1360800be9 100644 --- a/ext/socket/init.c +++ b/ext/socket/init.c @@ -222,7 +222,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(RB_IO_WAIT_READABLE, "recvfrom(2) would block"); } rb_sys_fail("recvfrom(2)"); } @@ -541,7 +541,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(RB_IO_WAIT_READABLE, "accept(2) would block"); } rb_sys_fail("accept(2)"); } diff --git a/ext/socket/socket.c b/ext/socket/socket.c index 9b9625485a..3d59b6cc96 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -446,7 +446,7 @@ sock_connect_nonblock(VALUE sock, VALUE addr) n = connect(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr)); if (n < 0) { if (errno == EINPROGRESS) - rb_mod_sys_fail(rb_mWaitWritable, "connect(2) would block"); + rb_readwrite_sys_fail(RB_IO_WAIT_WRITABLE, "connect(2) would block"); rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai); } diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index 5bd71121f6..43c5de6057 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -1354,6 +1354,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)); @@ -1373,6 +1374,10 @@ PRINTF_ARGS(void rb_sys_warning(const char*, ...), 1, 2); PRINTF_ARGS(void rb_warn(const char*, ...), 1, 2); PRINTF_ARGS(void rb_compile_warn(const char *, int, const char*, ...), 3, 4); +/* for rb_readwrite_sys_fail first argument */ +#define RB_IO_WAIT_READABLE 0 +#define RB_IO_WAIT_WRITABLE 1 + typedef VALUE rb_block_call_func(VALUE, VALUE, int, VALUE*); VALUE rb_each(VALUE); diff --git a/io.c b/io.c index ce6dc70af7..42388ac147 100644 --- a/io.c +++ b/io.c @@ -133,6 +133,16 @@ VALUE rb_eEOFError; VALUE rb_eIOError; VALUE rb_mWaitReadable; VALUE rb_mWaitWritable; +extern VALUE rb_eEAGAIN; +extern VALUE rb_eEWOULDBLOCK; +extern VALUE rb_eEINPROGRESS; + +static VALUE rb_eEAGAINWaitReadable; +static VALUE rb_eEAGAINWaitWritable; +static VALUE rb_eEWOULDBLOCKWaitReadable; +static VALUE rb_eEWOULDBLOCKWaitWritable; +static VALUE rb_eEINPROGRESSWaitWritable; +static VALUE rb_eEINPROGRESSWaitReadable; VALUE rb_stdin, rb_stdout, rb_stderr; VALUE rb_deferr; /* rescue VIM plugin */ @@ -2355,6 +2365,9 @@ rb_io_set_nonblock(rb_io_t *fptr) } } +void +rb_readwrite_sys_fail(int writable, const char *mesg); + static VALUE io_getpartial(int argc, VALUE *argv, VALUE io, int nonblock) { @@ -2393,7 +2406,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(RB_IO_WAIT_READABLE, "read would block"); rb_sys_fail_path(fptr->pathv); } } @@ -2612,7 +2625,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(RB_IO_WAIT_WRITABLE, "write would block"); rb_sys_fail_path(fptr->pathv); } @@ -11450,6 +11463,50 @@ 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 == RB_IO_WAIT_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 if (writable == RB_IO_WAIT_READABLE) { + 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); + } + } else { + rb_bug("invalid read/write type passed to rb_readwrite_sys_fail: %d", writable); + } +} + /* * Document-class: IOError * @@ -11658,6 +11715,25 @@ Init_IO(void) 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 */ diff --git a/test/openssl/test_pair.rb b/test/openssl/test_pair.rb index 12af6aeff5..fa36725b58 100644 --- a/test/openssl/test_pair.rb +++ b/test/openssl/test_pair.rb @@ -141,7 +141,7 @@ class OpenSSL::TestPair < Test::Unit::TestCase 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 diff --git a/test/socket/test_unix.rb b/test/socket/test_unix.rb index faf70f2e19..38d8333f2a 100644 --- a/test/socket/test_unix.rb +++ b/test/socket/test_unix.rb @@ -348,7 +348,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(IO::EAGAINWaitReadable === e) + assert(IO::WaitReadable === e) + end s2.send("", 0) s2.send("haha", 0) s2.send("", 0) @@ -357,7 +363,7 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase 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