ruby-changes:28143
From: headius <ko1@a...>
Date: Tue, 9 Apr 2013 04:59:06 +0900 (JST)
Subject: [ruby-changes:28143] headius:r40195 (trunk): Fix #6154 by introducing new EAGAIN/EWOULDBLOCK/EINPROGRESS
headius 2013-04-09 04:58:55 +0900 (Tue, 09 Apr 2013) New Revision: 40195 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=40195 Log: Fix #6154 by introducing new EAGAIN/EWOULDBLOCK/EINPROGRESS subclasses that include WaitReadable or WaitWritable rather than extending them into the exception object each time. * 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. Modified files: trunk/ChangeLog trunk/error.c trunk/ext/openssl/ossl_ssl.c trunk/ext/socket/ancdata.c trunk/ext/socket/init.c trunk/ext/socket/socket.c trunk/include/ruby/ruby.h trunk/io.c trunk/test/openssl/test_pair.rb trunk/test/socket/test_unix.rb Index: include/ruby/ruby.h =================================================================== --- include/ruby/ruby.h (revision 40194) +++ include/ruby/ruby.h (revision 40195) @@ -1354,6 +1354,7 @@ NORETURN(void rb_sys_fail(const char*)); https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1354 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 ch https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1374 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); Index: ChangeLog =================================================================== --- ChangeLog (revision 40194) +++ ChangeLog (revision 40195) @@ -1,3 +1,27 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Tue Apr 9 04:57:59 JST 2013 Charles Oliver Nutter <headius@h...> + + * 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 <nobu@r...> * ext/socket/extconf.rb: $defs needs -D or -U. nothing is added Index: io.c =================================================================== --- io.c (revision 40194) +++ io.c (revision 40195) @@ -133,6 +133,16 @@ VALUE rb_eEOFError; https://github.com/ruby/ruby/blob/trunk/io.c#L133 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) https://github.com/ruby/ruby/blob/trunk/io.c#L2365 } } +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, VAL https://github.com/ruby/ruby/blob/trunk/io.c#L2406 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 https://github.com/ruby/ruby/blob/trunk/io.c#L2625 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) https://github.com/ruby/ruby/blob/trunk/io.c#L11463 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) https://github.com/ruby/ruby/blob/trunk/io.c#L11715 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 40194) +++ ext/openssl/ossl_ssl.c (revision 40195) @@ -29,6 +29,9 @@ VALUE eSSLError; https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L29 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 https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L1233 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 https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L1242 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() https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L1847 * 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 40194) +++ ext/socket/init.c (revision 40195) @@ -222,7 +222,7 @@ rsock_s_recvfrom_nonblock(VALUE sock, in https://github.com/ruby/ruby/blob/trunk/ext/socket/init.c#L222 #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_ https://github.com/ruby/ruby/blob/trunk/ext/socket/init.c#L541 #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)"); } Index: ext/socket/socket.c =================================================================== --- ext/socket/socket.c (revision 40194) +++ ext/socket/socket.c (revision 40195) @@ -446,7 +446,7 @@ sock_connect_nonblock(VALUE sock, VALUE https://github.com/ruby/ruby/blob/trunk/ext/socket/socket.c#L446 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); } Index: ext/socket/ancdata.c =================================================================== --- ext/socket/ancdata.c (revision 40194) +++ ext/socket/ancdata.c (revision 40195) @@ -1285,7 +1285,7 @@ bsock_sendmsg_internal(int argc, VALUE * https://github.com/ruby/ruby/blob/trunk/ext/socket/ancdata.c#L1285 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 * https://github.com/ruby/ruby/blob/trunk/ext/socket/ancdata.c#L1600 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)) { /* Index: error.c =================================================================== --- error.c (revision 40194) +++ error.c (revision 40195) @@ -39,6 +39,10 @@ https://github.com/ruby/ruby/blob/trunk/error.c#L39 #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) https://github.com/ruby/ruby/blob/trunk/error.c#L1187 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 40194) +++ test/openssl/test_pair.rb (revision 40195) @@ -141,7 +141,7 @@ class OpenSSL::TestPair < Test::Unit::Te https://github.com/ruby/ruby/blob/trunk/test/openssl/test_pair.rb#L141 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 40194) +++ test/socket/test_unix.rb (revision 40195) @@ -348,7 +348,13 @@ class TestSocket_UNIXSocket < Test::Unit https://github.com/ruby/ruby/blob/trunk/test/socket/test_unix.rb#L348 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 https://github.com/ruby/ruby/blob/trunk/test/socket/test_unix.rb#L363 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 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/