dumping ground for random patches and texts
 help / color / mirror / Atom feed
* [PATCH] io.c: avoid kwarg parsing in C API
@ 2015-07-07 21:57 Eric Wong
  0 siblings, 0 replies; 2+ messages in thread
From: Eric Wong @ 2015-07-07 21:57 UTC (permalink / raw)
  To: spew

rb_scan_args and hash lookups for kwargs in the C API are clumsy and
slow.  Instead of improving the C API for performance, use Ruby
instead :)

Implement IO#read_nonblock and IO#write_nonblock in prelude.rb
to avoid argument parsing via rb_scan_args and hash lookups.

This speeds up IO#write_nonblock and IO#read_nonblock benchmarks
in both cases, including the original non-idiomatic case where
the `exception: false' hash is pre-allocated to avoid GC pressure.

Now, writing the kwargs in natural, idiomatic Ruby is fastest.
I've added the noex2 benchmark to show this.

target 0: a (ruby 2.3.0dev (2015-07-08 trunk 51190) [x86_64-linux]) at "a/ruby"
target 1: b (ruby 2.3.0dev (2015-07-08 nonblock-kwarg 51190) [x86_64-linux]) at "b/ruby"
-----------------------------------------------------------
raw data:

[["io_nonblock_noex",
  [[2.5436805468052626, 2.5724728293716908, 2.4915440678596497],
   [2.478000810369849, 2.4285155069082975, 2.462410459294915]]],
 ["io_nonblock_noex2",
  [[3.012514788657427, 3.034533655270934, 2.9972082190215588],
   [2.135501991957426, 2.146781364455819, 2.0429874528199434]]]]

Elapsed time: 30.348340944 (sec)
-----------------------------------------------------------
benchmark results:
minimum results in each 3 measurements.
Execution time (sec)
name    a       b
io_nonblock_noex        2.492   2.429
io_nonblock_noex2       2.997   2.043

Speedup ratio: compare with the result of `a' (greater is better)
name    b
io_nonblock_noex        1.026
io_nonblock_noex2       1.467

Note: I plan to followup commits for other *_nonblock methods
Eventually, I even wish to deprecate rb_scan_args :D

For what it's worth, I'm more excited about this change than usual
and hope to use prelude.rb more.
---
 benchmark/bm_io_nonblock_noex2.rb |  21 ++++++++
 io.c                              | 109 ++++++++++++++++++++++----------------
 prelude.rb                        |  10 ++++
 3 files changed, 94 insertions(+), 46 deletions(-)
 create mode 100644 benchmark/bm_io_nonblock_noex2.rb

diff --git a/benchmark/bm_io_nonblock_noex2.rb b/benchmark/bm_io_nonblock_noex2.rb
new file mode 100644
index 0000000..56819d0
--- /dev/null
+++ b/benchmark/bm_io_nonblock_noex2.rb
@@ -0,0 +1,21 @@
+nr = 1_000_000
+i = 0
+msg = '.'
+buf = '.'
+begin
+  r, w = IO.pipe
+  while i < nr
+    i += 1
+    w.write_nonblock(msg, exception: false)
+    r.read_nonblock(1, buf, exception: false)
+  end
+rescue ArgumentError # old Rubies
+  while i < nr
+    i += 1
+    w.write_nonblock(msg)
+    r.read_nonblock(1, buf)
+  end
+ensure
+  r.close
+  w.close
+end
diff --git a/io.c b/io.c
index 57ddbb7..5966ee1 100644
--- a/io.c
+++ b/io.c
@@ -2671,55 +2671,49 @@ io_readpartial(int argc, VALUE *argv, VALUE io)
  */
 
 static VALUE
-io_read_nonblock(int argc, VALUE *argv, VALUE io)
-{
-    VALUE ret, opts;
-
-    rb_scan_args(argc, argv, "11:", NULL, NULL, &opts);
-
-    ret = io_getpartial(argc, argv, io, opts, 1);
-
-    if (NIL_P(ret)) {
-	if (no_exception_p(opts))
-	    return Qnil;
-	else
-	    rb_eof_error();
-    }
-    return ret;
-}
-
-static VALUE
-io_write_nonblock(VALUE io, VALUE str, VALUE opts)
+io_read_nonblock(VALUE io, VALUE length, VALUE str, VALUE ex)
 {
     rb_io_t *fptr;
-    long n;
+    long n, len;
+    struct read_internal_arg arg;
 
-    if (!RB_TYPE_P(str, T_STRING))
-	str = rb_obj_as_string(str);
+    if ((len = NUM2LONG(length)) < 0) {
+	rb_raise(rb_eArgError, "negative length %ld given", len);
+    }
 
-    io = GetWriteIO(io);
+    io_setstrbuf(&str,len);
+    OBJ_TAINT(str);
     GetOpenFile(io, fptr);
-    rb_io_check_writable(fptr);
+    rb_io_check_byte_readable(fptr);
 
-    if (io_fflush(fptr) < 0)
-        rb_sys_fail(0);
+    if (len == 0)
+	return str;
 
-    rb_io_set_nonblock(fptr);
-    n = write(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str));
+    n = read_buffered_data(RSTRING_PTR(str), len, fptr);
+    if (n <= 0) {
+	rb_io_set_nonblock(fptr);
+	io_setstrbuf(&str, len);
+	arg.fd = fptr->fd;
+	arg.str_ptr = RSTRING_PTR(str);
+	arg.len = len;
+	rb_str_locktmp_ensure(str, read_internal_call, (VALUE)&arg);
+	n = arg.len;
+        if (n < 0) {
+            if ((errno == EWOULDBLOCK || errno == EAGAIN)) {
+                if (ex == Qfalse) return sym_wait_readable;
+                rb_readwrite_sys_fail(RB_IO_WAIT_READABLE, "read would block");
+            }
+            rb_sys_fail_path(fptr->pathv);
+        }
+    }
+    io_set_read_length(str, n);
 
-    if (n == -1) {
-        if (errno == EWOULDBLOCK || errno == EAGAIN) {
-	    if (no_exception_p(opts)) {
-		return sym_wait_writable;
-	    }
-	    else {
-		rb_readwrite_sys_fail(RB_IO_WAIT_WRITABLE, "write would block");
-	    }
-	}
-        rb_sys_fail_path(fptr->pathv);
+    if (n == 0) {
+	if (ex == Qfalse) return Qnil;
+	rb_eof_error();
     }
 
-    return LONG2FIX(n);
+    return str;
 }
 
 /*
@@ -2779,15 +2773,38 @@ io_write_nonblock(VALUE io, VALUE str, VALUE opts)
  *  return the symbol :wait_writable instead.
  *
  */
-
 static VALUE
-rb_io_write_nonblock(int argc, VALUE *argv, VALUE io)
+io_write_nonblock(VALUE io, VALUE str, VALUE ex)
 {
-    VALUE str, opts;
+    rb_io_t *fptr;
+    long n;
 
-    rb_scan_args(argc, argv, "10:", &str, &opts);
+    if (!RB_TYPE_P(str, T_STRING))
+	str = rb_obj_as_string(str);
 
-    return io_write_nonblock(io, str, opts);
+    io = GetWriteIO(io);
+    GetOpenFile(io, fptr);
+    rb_io_check_writable(fptr);
+
+    if (io_fflush(fptr) < 0)
+        rb_sys_fail(0);
+
+    rb_io_set_nonblock(fptr);
+    n = write(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str));
+
+    if (n == -1) {
+        if (errno == EWOULDBLOCK || errno == EAGAIN) {
+	    if (ex == Qfalse) {
+		return sym_wait_writable;
+	    }
+	    else {
+		rb_readwrite_sys_fail(RB_IO_WAIT_WRITABLE, "write would block");
+	    }
+	}
+        rb_sys_fail_path(fptr->pathv);
+    }
+
+    return LONG2FIX(n);
 }
 
 /*
@@ -12246,8 +12263,8 @@ Init_IO(void)
 
     rb_define_method(rb_cIO, "readlines",  rb_io_readlines, -1);
 
-    rb_define_method(rb_cIO, "read_nonblock",  io_read_nonblock, -1);
-    rb_define_method(rb_cIO, "write_nonblock", rb_io_write_nonblock, -1);
+    rb_define_private_method(rb_cIO, "__read_nonblock", io_read_nonblock, 3);
+    rb_define_private_method(rb_cIO, "__write_nonblock", io_write_nonblock, 2);
     rb_define_method(rb_cIO, "readpartial",  io_readpartial, -1);
     rb_define_method(rb_cIO, "read",  io_read, -1);
     rb_define_method(rb_cIO, "write", io_write_m, 1);
diff --git a/prelude.rb b/prelude.rb
index cc24a81..1a2e41b 100644
--- a/prelude.rb
+++ b/prelude.rb
@@ -13,3 +13,13 @@ class Thread
     }
   end
 end
+
+class IO
+  def read_nonblock(len, buf = nil, exception: true)
+    __read_nonblock(len, buf, exception)
+  end
+
+  def write_nonblock(buf, exception: true)
+    __write_nonblock(buf, exception)
+  end
+end
-- 
EW


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [PATCH] io.c: avoid kwarg parsing in C API
@ 2015-11-12  1:51 Eric Wong
  0 siblings, 0 replies; 2+ messages in thread
From: Eric Wong @ 2015-11-12  1:51 UTC (permalink / raw)
  To: spew

* benchmark/bm_io_nonblock_noex2.rb: new benchmark based
  on bm_io_nonblock_noex.rb
* io.c (io_read_nonblock): move documentation to prelude.rb
  (io_write_nonblock): ditto
  (Init_io): private, internal methods for prelude.rb use only
* prelude.rb (IO#read_nonblock): wrapper + documentation
  (IO#write_nonblock): ditto

rb_scan_args and hash lookups for kwargs in the C API are clumsy and
slow.  Instead of improving the C API for performance, use Ruby
instead :)

Implement IO#read_nonblock and IO#write_nonblock in prelude.rb
to avoid argument parsing via rb_scan_args and hash lookups.

This speeds up IO#write_nonblock and IO#read_nonblock benchmarks
in both cases, including the original non-idiomatic case where
the `exception: false' hash is pre-allocated to avoid GC pressure.

Now, writing the kwargs in natural, idiomatic Ruby is fastest.
I've added the noex2 benchmark to show this.

2015-11-12 01:41:12 +0000
target 0: a (ruby 2.3.0dev (2015-11-11 trunk 52540) [x86_64-linux])
target 1: b (ruby 2.3.0dev (2015-11-11 avoid-kwarg-capi 52540)
-----------------------------------------------------------
benchmark results:
minimum results in each 10 measurements.
Execution time (sec)
name              a       b
io_nonblock_noex    2.508   2.382
io_nonblock_noex2   2.950   1.882

Speedup ratio: compare with the result of `a' (greater is better)
name              b
io_nonblock_noex    1.053
io_nonblock_noex2   1.567
---
 benchmark/bm_io_nonblock_noex2.rb |  21 +++++
 io.c                              | 176 ++++++++++----------------------------
 prelude.rb                        | 114 ++++++++++++++++++++++++
 3 files changed, 181 insertions(+), 130 deletions(-)
 create mode 100644 benchmark/bm_io_nonblock_noex2.rb

diff --git a/benchmark/bm_io_nonblock_noex2.rb b/benchmark/bm_io_nonblock_noex2.rb
new file mode 100644
index 0000000..56819d0
--- /dev/null
+++ b/benchmark/bm_io_nonblock_noex2.rb
@@ -0,0 +1,21 @@
+nr = 1_000_000
+i = 0
+msg = '.'
+buf = '.'
+begin
+  r, w = IO.pipe
+  while i < nr
+    i += 1
+    w.write_nonblock(msg, exception: false)
+    r.read_nonblock(1, buf, exception: false)
+  end
+rescue ArgumentError # old Rubies
+  while i < nr
+    i += 1
+    w.write_nonblock(msg)
+    r.read_nonblock(1, buf)
+  end
+ensure
+  r.close
+  w.close
+end
diff --git a/io.c b/io.c
index a192257..0606b8e 100644
--- a/io.c
+++ b/io.c
@@ -2632,74 +2632,56 @@ io_nonblock_eof(VALUE opts)
     return Qnil;
 }
 
-/*
- *  call-seq:
- *     ios.read_nonblock(maxlen)              -> string
- *     ios.read_nonblock(maxlen, outbuf)      -> outbuf
- *
- *  Reads at most <i>maxlen</i> bytes from <em>ios</em> using
- *  the read(2) system call after O_NONBLOCK is set for
- *  the underlying file descriptor.
- *
- *  If the optional <i>outbuf</i> argument is present,
- *  it must reference a String, which will receive the data.
- *  The <i>outbuf</i> will contain only the received data after the method call
- *  even if it is not empty at the beginning.
- *
- *  read_nonblock just calls the read(2) system call.
- *  It causes all errors the read(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc.
- *  The caller should care such errors.
- *
- *  If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
- *  it is extended by IO::WaitReadable.
- *  So IO::WaitReadable can be used to rescue the exceptions for retrying read_nonblock.
- *
- *  read_nonblock causes EOFError on EOF.
- *
- *  If the read byte buffer is not empty,
- *  read_nonblock reads from the buffer like readpartial.
- *  In this case, the read(2) system call is not called.
- *
- *  When read_nonblock raises an exception kind of IO::WaitReadable,
- *  read_nonblock should not be called
- *  until io is readable for avoiding busy loop.
- *  This can be done as follows.
- *
- *    # emulates blocking read (readpartial).
- *    begin
- *      result = io.read_nonblock(maxlen)
- *    rescue IO::WaitReadable
- *      IO.select([io])
- *      retry
- *    end
- *
- *  Although IO#read_nonblock doesn't raise IO::WaitWritable.
- *  OpenSSL::Buffering#read_nonblock can raise IO::WaitWritable.
- *  If IO and SSL should be used polymorphically,
- *  IO::WaitWritable should be rescued too.
- *  See the document of OpenSSL::Buffering#read_nonblock for sample code.
- *
- *  Note that this method is identical to readpartial
- *  except the non-blocking flag is set.
- */
-
+/* :nodoc: */
 static VALUE
-io_read_nonblock(int argc, VALUE *argv, VALUE io)
+io_read_nonblock(VALUE io, VALUE length, VALUE str, VALUE ex)
 {
-    VALUE ret, opts;
+    rb_io_t *fptr;
+    long n, len;
+    struct read_internal_arg arg;
 
-    rb_scan_args(argc, argv, "11:", NULL, NULL, &opts);
+    if ((len = NUM2LONG(length)) < 0) {
+	rb_raise(rb_eArgError, "negative length %ld given", len);
+    }
 
-    ret = io_getpartial(argc, argv, io, opts, 1);
+    io_setstrbuf(&str,len);
+    OBJ_TAINT(str);
+    GetOpenFile(io, fptr);
+    rb_io_check_byte_readable(fptr);
 
-    if (NIL_P(ret)) {
-	return io_nonblock_eof(opts);
+    if (len == 0)
+	return str;
+
+    n = read_buffered_data(RSTRING_PTR(str), len, fptr);
+    if (n <= 0) {
+	rb_io_set_nonblock(fptr);
+	io_setstrbuf(&str, len);
+	arg.fd = fptr->fd;
+	arg.str_ptr = RSTRING_PTR(str);
+	arg.len = len;
+	rb_str_locktmp_ensure(str, read_internal_call, (VALUE)&arg);
+	n = arg.len;
+        if (n < 0) {
+            if ((errno == EWOULDBLOCK || errno == EAGAIN)) {
+                if (ex == Qfalse) return sym_wait_readable;
+                rb_readwrite_sys_fail(RB_IO_WAIT_READABLE, "read would block");
+            }
+            rb_sys_fail_path(fptr->pathv);
+        }
     }
-    return ret;
+    io_set_read_length(str, n);
+
+    if (n == 0) {
+	if (ex == Qfalse) return Qnil;
+	rb_eof_error();
+    }
+
+    return str;
 }
 
+/* :nodoc: */
 static VALUE
-io_write_nonblock(VALUE io, VALUE str, VALUE opts)
+io_write_nonblock(VALUE io, VALUE str, VALUE ex)
 {
     rb_io_t *fptr;
     long n;
@@ -2719,7 +2701,7 @@ io_write_nonblock(VALUE io, VALUE str, VALUE opts)
 
     if (n == -1) {
         if (errno == EWOULDBLOCK || errno == EAGAIN) {
-	    if (no_exception_p(opts)) {
+	    if (ex == Qfalse) {
 		return sym_wait_writable;
 	    }
 	    else {
@@ -2734,74 +2716,6 @@ io_write_nonblock(VALUE io, VALUE str, VALUE opts)
 
 /*
  *  call-seq:
- *     ios.write_nonblock(string)   -> integer
- *     ios.write_nonblock(string [, options])   -> integer
- *
- *  Writes the given string to <em>ios</em> using
- *  the write(2) system call after O_NONBLOCK is set for
- *  the underlying file descriptor.
- *
- *  It returns the number of bytes written.
- *
- *  write_nonblock just calls the write(2) system call.
- *  It causes all errors the write(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc.
- *  The result may also be smaller than string.length (partial write).
- *  The caller should care such errors and partial write.
- *
- *  If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
- *  it is extended by IO::WaitWritable.
- *  So IO::WaitWritable can be used to rescue the exceptions for retrying write_nonblock.
- *
- *    # Creates a pipe.
- *    r, w = IO.pipe
- *
- *    # write_nonblock writes only 65536 bytes and return 65536.
- *    # (The pipe size is 65536 bytes on this environment.)
- *    s = "a" * 100000
- *    p w.write_nonblock(s)     #=> 65536
- *
- *    # write_nonblock cannot write a byte and raise EWOULDBLOCK (EAGAIN).
- *    p w.write_nonblock("b")   # Resource temporarily unavailable (Errno::EAGAIN)
- *
- *  If the write buffer is not empty, it is flushed at first.
- *
- *  When write_nonblock raises an exception kind of IO::WaitWritable,
- *  write_nonblock should not be called
- *  until io is writable for avoiding busy loop.
- *  This can be done as follows.
- *
- *    begin
- *      result = io.write_nonblock(string)
- *    rescue IO::WaitWritable, Errno::EINTR
- *      IO.select(nil, [io])
- *      retry
- *    end
- *
- *  Note that this doesn't guarantee to write all data in string.
- *  The length written is reported as result and it should be checked later.
- *
- *  On some platforms such as Windows, write_nonblock is not supported
- *  according to the kind of the IO object.
- *  In such cases, write_nonblock raises <code>Errno::EBADF</code>.
- *
- *  By specifying `exception: false`, the options hash allows you to indicate
- *  that write_nonblock should not raise an IO::WaitWritable exception, but
- *  return the symbol :wait_writable instead.
- *
- */
-
-static VALUE
-rb_io_write_nonblock(int argc, VALUE *argv, VALUE io)
-{
-    VALUE str, opts;
-
-    rb_scan_args(argc, argv, "10:", &str, &opts);
-
-    return io_write_nonblock(io, str, opts);
-}
-
-/*
- *  call-seq:
  *     ios.read([length [, outbuf]])    -> string, outbuf, or nil
  *
  *  Reads <i>length</i> bytes from the I/O stream.
@@ -12386,8 +12300,10 @@ Init_IO(void)
 
     rb_define_method(rb_cIO, "readlines",  rb_io_readlines, -1);
 
-    rb_define_method(rb_cIO, "read_nonblock",  io_read_nonblock, -1);
-    rb_define_method(rb_cIO, "write_nonblock", rb_io_write_nonblock, -1);
+    /* for prelude.rb use only: */
+    rb_define_private_method(rb_cIO, "__read_nonblock", io_read_nonblock, 3);
+    rb_define_private_method(rb_cIO, "__write_nonblock", io_write_nonblock, 2);
+
     rb_define_method(rb_cIO, "readpartial",  io_readpartial, -1);
     rb_define_method(rb_cIO, "read",  io_read, -1);
     rb_define_method(rb_cIO, "write", io_write_m, 1);
diff --git a/prelude.rb b/prelude.rb
index cc24a81..20a9e2d 100644
--- a/prelude.rb
+++ b/prelude.rb
@@ -13,3 +13,117 @@ class Thread
     }
   end
 end
+
+class IO
+
+  # call-seq:
+  #    ios.read_nonblock(maxlen)              -> string
+  #    ios.read_nonblock(maxlen, outbuf)      -> outbuf
+  #
+  # Reads at most <i>maxlen</i> bytes from <em>ios</em> using
+  # the read(2) system call after O_NONBLOCK is set for
+  # the underlying file descriptor.
+  #
+  # If the optional <i>outbuf</i> argument is present,
+  # it must reference a String, which will receive the data.
+  # The <i>outbuf</i> will contain only the received data after the method call
+  # even if it is not empty at the beginning.
+  #
+  # read_nonblock just calls the read(2) system call.
+  # It causes all errors the read(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc.
+  # The caller should care such errors.
+  #
+  # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
+  # it is extended by IO::WaitReadable.
+  # So IO::WaitReadable can be used to rescue the exceptions for retrying
+  # read_nonblock.
+  #
+  # read_nonblock causes EOFError on EOF.
+  #
+  # If the read byte buffer is not empty,
+  # read_nonblock reads from the buffer like readpartial.
+  # In this case, the read(2) system call is not called.
+  #
+  # When read_nonblock raises an exception kind of IO::WaitReadable,
+  # read_nonblock should not be called
+  # until io is readable for avoiding busy loop.
+  # This can be done as follows.
+  #
+  #   # emulates blocking read (readpartial).
+  #   begin
+  #     result = io.read_nonblock(maxlen)
+  #   rescue IO::WaitReadable
+  #     IO.select([io])
+  #     retry
+  #   end
+  #
+  # Although IO#read_nonblock doesn't raise IO::WaitWritable.
+  # OpenSSL::Buffering#read_nonblock can raise IO::WaitWritable.
+  # If IO and SSL should be used polymorphically,
+  # IO::WaitWritable should be rescued too.
+  # See the document of OpenSSL::Buffering#read_nonblock for sample code.
+  #
+  # Note that this method is identical to readpartial
+  # except the non-blocking flag is set.
+  def read_nonblock(len, buf = nil, exception: true)
+    __read_nonblock(len, buf, exception)
+  end
+
+  # call-seq:
+  #    ios.write_nonblock(string)   -> integer
+  #    ios.write_nonblock(string [, options])   -> integer
+  #
+  # Writes the given string to <em>ios</em> using
+  # the write(2) system call after O_NONBLOCK is set for
+  # the underlying file descriptor.
+  #
+  # It returns the number of bytes written.
+  #
+  # write_nonblock just calls the write(2) system call.
+  # It causes all errors the write(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc.
+  # The result may also be smaller than string.length (partial write).
+  # The caller should care such errors and partial write.
+  #
+  # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
+  # it is extended by IO::WaitWritable.
+  # So IO::WaitWritable can be used to rescue the exceptions for retrying write_nonblock.
+  #
+  #   # Creates a pipe.
+  #   r, w = IO.pipe
+  #
+  #   # write_nonblock writes only 65536 bytes and return 65536.
+  #   # (The pipe size is 65536 bytes on this environment.)
+  #   s = "a"  #100000
+  #   p w.write_nonblock(s)     #=> 65536
+  #
+  #   # write_nonblock cannot write a byte and raise EWOULDBLOCK (EAGAIN).
+  #   p w.write_nonblock("b")   # Resource temporarily unavailable (Errno::EAGAIN)
+  #
+  # If the write buffer is not empty, it is flushed at first.
+  #
+  # When write_nonblock raises an exception kind of IO::WaitWritable,
+  # write_nonblock should not be called
+  # until io is writable for avoiding busy loop.
+  # This can be done as follows.
+  #
+  #   begin
+  #     result = io.write_nonblock(string)
+  #   rescue IO::WaitWritable, Errno::EINTR
+  #     IO.select(nil, [io])
+  #     retry
+  #   end
+  #
+  # Note that this doesn't guarantee to write all data in string.
+  # The length written is reported as result and it should be checked later.
+  #
+  # On some platforms such as Windows, write_nonblock is not supported
+  # according to the kind of the IO object.
+  # In such cases, write_nonblock raises <code>Errno::EBADF</code>.
+  #
+  # By specifying `exception: false`, the options hash allows you to indicate
+  # that write_nonblock should not raise an IO::WaitWritable exception, but
+  # return the symbol :wait_writable instead.
+  def write_nonblock(buf, exception: true)
+    __write_nonblock(buf, exception)
+  end
+end
-- 
EW


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2015-11-12  1:51 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-07 21:57 [PATCH] io.c: avoid kwarg parsing in C API Eric Wong
  -- strict thread matches above, loose matches on Subject: below --
2015-11-12  1:51 Eric Wong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).