about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-05-17 17:12:48 -0700
committerEric Wong <normalperson@yhbt.net>2011-05-17 17:12:48 -0700
commit1e4b4af9d8fa01b59b933fe94b405288f668a7fc (patch)
tree0372696b6ff67008f093298f18359d02f6342110
parent86508cdc09ef36d8c5ae708ef9e3e19a82844e98 (diff)
downloadruby_io_splice-1e4b4af9d8fa01b59b933fe94b405288f668a7fc.tar.gz
We should warn our users to avoid a blocking splice() from a
socket into a pipe if the socket buffers are full unless there's
a blocking read/splice on the other end of the pipe.
-rw-r--r--ext/io_splice/io_splice_ext.c14
-rw-r--r--lib/io/splice.rb7
2 files changed, 18 insertions, 3 deletions
diff --git a/ext/io_splice/io_splice_ext.c b/ext/io_splice/io_splice_ext.c
index ffccef1..23cc34f 100644
--- a/ext/io_splice/io_splice_ext.c
+++ b/ext/io_splice/io_splice_ext.c
@@ -217,9 +217,9 @@ static ssize_t do_splice(int argc, VALUE *argv, unsigned dflags)
  * into account userspace buffering done by Ruby or stdio.  It is
  * also not subject to encoding/decoding filters under Ruby 1.9.
  *
- * Consider using IO.trysplice if you are using non-blocking I/O on
- * both descriptors as it avoids the cost of raising common Errno::EAGAIN
- * exceptions.
+ * Consider using IO.trysplice if +io_out+ is a pipe or if you are using
+ * non-blocking I/O on both descriptors as it avoids the cost of raising
+ * common Errno::EAGAIN exceptions.
  *
  * See manpage for full documentation:
  * http://kernel.org/doc/man-pages/online/pages/man2/splice.2.html
@@ -247,6 +247,8 @@ static VALUE my_splice(int argc, VALUE *argv, VALUE self)
  * but this can still block if the non-pipe descriptor is blocking.
  *
  * See IO.splice documentation for more details.
+ *
+ * This method is recommended whenever +io_out+ is a pipe.
  */
 static VALUE trysplice(int argc, VALUE *argv, VALUE self)
 {
@@ -639,6 +641,12 @@ void Init_io_splice_ext(void)
          * themselves are ignored by this family of functions, and
          * using this flag is the only way to get non-blocking operation
          * out of them.
+         *
+         * It is highly recommended this flag be set (or IO.trysplice used)
+         * whenever splicing from a socket into a pipe unless there is
+         * another (native) thread or process doing a blocking read on that
+         * pipe.  Otherwise it is possible to block a single-threaded process
+         * if the socket buffers are larger than the pipe buffers.
          */
         rb_define_const(mSplice, "F_NONBLOCK", UINT2NUM(SPLICE_F_NONBLOCK));
         assert(WAITALL != SPLICE_F_NONBLOCK && "WAITALL == F_NONBLOCK");
diff --git a/lib/io/splice.rb b/lib/io/splice.rb
index 89fab92..4b666f8 100644
--- a/lib/io/splice.rb
+++ b/lib/io/splice.rb
@@ -84,6 +84,13 @@ module IO::Splice
   # This will block and wait for IO completion of +len+
   # Raises +EOFError+ if end of file is reached.
   # bytes.  Returns the number of bytes actually spliced (always +len+)
+  # unless +src+ does not have +len+ bytes to read.
+  #
+  # Do not use this method to splice a socket +src+ into a pipe +dst+
+  # unless there is another process or native thread doing a blocking
+  # read on the other end of the +dst+ pipe.
+  #
+  # This method is safe for splicing a pipe +src+ into any type of +dst+ IO.
   def self.full(src, dst, len, src_offset)
     IO.splice(src, src_offset, dst, nil, len, F_MOVE | WAITALL)
   end