[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]