about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-05-18 15:36:52 -0700
committerEric Wong <normalperson@yhbt.net>2011-05-18 15:54:57 -0700
commita2ae924fb1c372cc559a59feca40780a9a37ba33 (patch)
tree905a2cca5553c158060a1407b73bed8303b0e469
parentaad5139884ad055d8aafb4316360e9f486b2d452 (diff)
downloadruby_io_splice-a2ae924fb1c372cc559a59feca40780a9a37ba33.tar.gz
limit maximum splice length to 1 << 30
This is the same value haproxy has to work around the same
issue on 64-bit platforms.

ref: a9de333aa58e6cb76f08a50e8ba2c5931184068f in
     http://git.1wt.eu/git/haproxy.git
-rw-r--r--ext/io_splice/io_splice_ext.c17
-rw-r--r--lib/io/splice.rb7
-rw-r--r--test/test_tcp_splice.rb17
3 files changed, 38 insertions, 3 deletions
diff --git a/ext/io_splice/io_splice_ext.c b/ext/io_splice/io_splice_ext.c
index 23cc34f..cbbdbfc 100644
--- a/ext/io_splice/io_splice_ext.c
+++ b/ext/io_splice/io_splice_ext.c
@@ -15,6 +15,9 @@
 static VALUE sym_EAGAIN;
 #define WAITALL 0x4000000
 
+/* taken from haproxy */
+#define MAX_AT_ONCE (1 << 30)
+
 #ifndef F_LINUX_SPECIFIC_BASE
 #  define F_LINUX_SPECIFIC_BASE 1024
 #endif
@@ -130,6 +133,9 @@ static VALUE nogvl_splice(void *ptr)
 {
         struct splice_args *a = ptr;
 
+        if (a->len > MAX_AT_ONCE)
+                a->len = MAX_AT_ONCE;
+
         return (VALUE)splice(a->fd_in, a->off_in, a->fd_out, a->off_out,
                              a->len, a->flags);
 }
@@ -276,6 +282,9 @@ static VALUE nogvl_tee(void *ptr)
 {
         struct tee_args *a = ptr;
 
+        if (a->len > MAX_AT_ONCE)
+                a->len = MAX_AT_ONCE;
+
         return (VALUE)tee(a->fd_in, a->fd_out, a->len, a->flags);
 }
 
@@ -682,6 +691,14 @@ void Init_io_splice_ext(void)
          */
         rb_define_const(mSplice, "PIPE_BUF", UINT2NUM(PIPE_BUF));
 
+        /*
+         * The maximum size we're allowed to splice at once.  Larger
+         * sizes will be broken up and retried if the WAITALL flag or
+         * IO::Splice.copy_stream is used.
+         */
+        rb_define_const(mSplice, "MAX_AT_ONCE", SIZET2NUM(MAX_AT_ONCE));
+
+
         if (uname(&utsname) == -1)
                 rb_sys_fail("uname");
 
diff --git a/lib/io/splice.rb b/lib/io/splice.rb
index 84bca5e..65d8ed4 100644
--- a/lib/io/splice.rb
+++ b/lib/io/splice.rb
@@ -49,21 +49,22 @@ module IO::Splice
     if src.stat.pipe? || dst.stat.pipe?
       return full(src, dst, len, src_offset) if len
       rv = 0
-      while n = partial(src, dst, PIPE_CAPA, src_offset)
+      while n = partial(src, dst, MAX_AT_ONCE, src_offset)
         rv += n
         src_offset += n if src_offset
       end
     else
       r, w = tmp = IO.pipe
       close.concat(tmp)
+      rv = 0
       if len
         while len != 0 && n = partial(src, w, len, src_offset)
           src_offset += n if src_offset
+          rv += n
           len -= full(r, dst, n, nil)
         end
       else
-        rv = 0
-        while n = partial(src, w, PIPE_CAPA, src_offset)
+        while n = partial(src, w, MAX_AT_ONCE, src_offset)
           src_offset += n if src_offset
           rv += full(r, dst, n, nil)
         end
diff --git a/test/test_tcp_splice.rb b/test/test_tcp_splice.rb
index 2ddbfcb..686333c 100644
--- a/test/test_tcp_splice.rb
+++ b/test/test_tcp_splice.rb
@@ -46,4 +46,21 @@ class TestTCPCopyStream < Test::Unit::TestCase
     bytes = IO::Splice.copy_stream(@accept, "/dev/null", expect)
     assert_equal expect, bytes
   end
+
+  def test_mega_splice
+    nr = 2000
+    buf = '0123456789abcdef' * 1024
+    expect = buf.size * nr
+    thr = Thread.new do
+      nr.times { @client.write(buf) }
+      @client.close
+    end
+    size_t_max = if (1 << 30).kind_of?(Bignum)
+      0xffffffff
+    else
+      0xffffffffffffffff
+    end
+    bytes = IO::Splice.copy_stream(@accept, "/dev/null", size_t_max)
+    assert_equal expect, bytes
+  end
 end