about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-01-31 15:18:33 -0800
committerEric Wong <normalperson@yhbt.net>2011-01-31 15:32:10 -0800
commit6479b6d3934b8930910e0057f516aa019dd7a8c7 (patch)
tree625b34639cc1819cde2e0f013637efcda161f219
parent15744a90cda72e9007914cd2a78b0b2949193479 (diff)
downloadkgio-6479b6d3934b8930910e0057f516aa019dd7a8c7.tar.gz
Hopefully it works for people who use TCP_NOPUSH...
-rw-r--r--ext/kgio/autopush.c36
-rw-r--r--test/test_autopush.rb119
2 files changed, 105 insertions, 50 deletions
diff --git a/ext/kgio/autopush.c b/ext/kgio/autopush.c
index ac9e217..fa24055 100644
--- a/ext/kgio/autopush.c
+++ b/ext/kgio/autopush.c
@@ -13,6 +13,20 @@
  */
 
 #include "kgio.h"
+#include <netinet/tcp.h>
+
+/*
+ * As of FreeBSD 4.5, TCP_NOPUSH == TCP_CORK
+ * ref: http://dotat.at/writing/nopush.html
+ * We won't care for older FreeBSD since nobody runs Ruby on them...
+ */
+#ifdef TCP_CORK
+#  define KGIO_NOPUSH TCP_CORK
+#elif defined(TCP_NOPUSH)
+#  define KGIO_NOPUSH TCP_NOPUSH
+#endif
+
+#ifdef KGIO_NOPUSH
 static ID id_autopush_state;
 static int enabled;
 
@@ -98,8 +112,6 @@ void kgio_autopush_recv(VALUE io)
         }
 }
 
-#ifdef __linux__
-#include <netinet/tcp.h>
 static enum autopush_state detect_acceptor_state(VALUE io)
 {
         int corked = 0;
@@ -107,9 +119,9 @@ static enum autopush_state detect_acceptor_state(VALUE io)
         socklen_t optlen = sizeof(int);
         enum autopush_state state;
 
-        if (getsockopt(fd, SOL_TCP, TCP_CORK, &corked, &optlen) != 0) {
+        if (getsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &corked, &optlen) != 0) {
                 if (errno != EOPNOTSUPP)
-                        rb_sys_fail("getsockopt(SOL_TCP, TCP_CORK)");
+                        rb_sys_fail("getsockopt(TCP_CORK/TCP_NOPUSH)");
                 errno = 0;
                 state = AUTOPUSH_STATE_ACCEPTOR_IGNORE;
         } else if (corked) {
@@ -132,13 +144,15 @@ static void push_pending_data(VALUE io)
         const socklen_t optlen = sizeof(int);
         const int fd = my_fileno(io);
 
-        if (setsockopt(fd, SOL_TCP, TCP_CORK, &optval, optlen) != 0)
-                rb_sys_fail("setsockopt(SOL_TCP, TCP_CORK, 0)");
+        if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
+                rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 0)");
         /* immediately recork */
         optval = 1;
-        if (setsockopt(fd, SOL_TCP, TCP_CORK, &optval, optlen) != 0)
-                rb_sys_fail("setsockopt(SOL_TCP, TCP_CORK, 1)");
+        if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
+                rb_sys_fail("setsockopt(TCP_CORK, 1)");
 }
-/* TODO: add FreeBSD support */
-
-#endif /* linux */
+#else /* !KGIO_NOPUSH */
+void init_kgio_autopush(void)
+{
+}
+#endif /* ! KGIO_NOPUSH */
diff --git a/test/test_autopush.rb b/test/test_autopush.rb
index 2fa49be..4d15b7b 100644
--- a/test/test_autopush.rb
+++ b/test/test_autopush.rb
@@ -6,6 +6,7 @@ require 'kgio'
 
 class TestAutopush < Test::Unit::TestCase
   TCP_CORK = 3
+  TCP_NOPUSH = 4
 
   def setup
     Kgio.autopush = false
@@ -14,8 +15,11 @@ class TestAutopush < Test::Unit::TestCase
     @host = ENV["TEST_HOST"] || '127.0.0.1'
     @srv = Kgio::TCPServer.new(@host, 0)
     assert_nothing_raised {
-      @srv.setsockopt(Socket::SOL_TCP, TCP_CORK, 1)
+      @srv.setsockopt(Socket::IPPROTO_TCP, TCP_CORK, 1)
     } if RUBY_PLATFORM =~ /linux/
+    assert_nothing_raised {
+      @srv.setsockopt(Socket::IPPROTO_TCP, TCP_NOPUSH, 1)
+    } if RUBY_PLATFORM =~ /freebsd/
     @port = @srv.addr[1]
   end
 
@@ -27,16 +31,30 @@ class TestAutopush < Test::Unit::TestCase
     tmp.close rescue nil
     @srv = Kgio::UNIXServer.new(@path)
     @rd = Kgio::UNIXSocket.new(@path)
-    io, err = Strace.me { @wr = @srv.kgio_accept }
-    assert_nil err
-    rc = nil
-    io, err = Strace.me {
-      @wr.kgio_write "HI\n"
-      rc = @wr.kgio_tryread 666
-    }
-    assert_nil err
-    lines = io.readlines
-    assert lines.grep(/TCP_CORK/).empty?, lines.inspect
+    t0 = nil
+    if defined?(Strace)
+      io, err = Strace.me { @wr = @srv.kgio_accept }
+      assert_nil err
+      rc = nil
+      io, err = Strace.me {
+        t0 = Time.now
+        @wr.kgio_write "HI\n"
+        rc = @wr.kgio_tryread 666
+      }
+      assert_nil err
+      lines = io.readlines
+      assert lines.grep(/TCP_CORK/).empty?, lines.inspect
+    else
+      assert_nothing_raised do
+        @wr = @srv.kgio_accept
+        t0 = Time.now
+        @wr.kgio_write "HI\n"
+        rc = @wr.kgio_tryread 666
+      end
+    end
+    assert_equal "HI\n", @rd.kgio_read(3)
+    diff = Time.now - t0
+    assert(diff < 0.200, "nopush on UNIX sockets? diff=#{diff} > 200ms")
     assert_equal :wait_readable, rc
   ensure
     File.unlink(@path) rescue nil
@@ -47,64 +65,87 @@ class TestAutopush < Test::Unit::TestCase
     assert_equal false, Kgio.autopush?
 
     @wr = Kgio::TCPSocket.new(@host, @port)
-    io, err = Strace.me { @rd = @srv.kgio_accept }
-    assert_nil err
-    lines = io.readlines
-    assert lines.grep(/TCP_CORK/).empty?, lines.inspect
-    assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0]
+    if defined?(Strace)
+      io, err = Strace.me { @rd = @srv.kgio_accept }
+      assert_nil err
+      lines = io.readlines
+      assert lines.grep(/TCP_CORK/).empty?, lines.inspect
+      assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0]
+    else
+      @rd = @srv.kgio_accept
+    end
 
     rbuf = "..."
     t0 = Time.now
     @rd.kgio_write "HI\n"
     @wr.kgio_read(3, rbuf)
     diff = Time.now - t0
-    assert(diff >= 0.200, "TCP_CORK broken? diff=#{diff} > 200ms")
+    assert(diff >= 0.200, "nopush broken? diff=#{diff} > 200ms")
     assert_equal "HI\n", rbuf
-  end if RUBY_PLATFORM =~ /linux/
+  end
 
   def test_autopush_true
     Kgio.autopush = true
     assert_equal true, Kgio.autopush?
     @wr = Kgio::TCPSocket.new(@host, @port)
-    io, err = Strace.me { @rd = @srv.kgio_accept }
-    assert_nil err
-    lines = io.readlines
-    assert_equal 1, lines.grep(/TCP_CORK/).size, lines.inspect
-    assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0]
+
+    if defined?(Strace)
+      io, err = Strace.me { @rd = @srv.kgio_accept }
+      assert_nil err
+      lines = io.readlines
+      assert_equal 1, lines.grep(/TCP_CORK/).size, lines.inspect
+      assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0]
+    else
+      @rd = @srv.kgio_accept
+    end
 
     @wr.write "HI\n"
     rbuf = ""
-    io, err = Strace.me { @rd.kgio_read(3, rbuf) }
-    assert_nil err
-    lines = io.readlines
-    assert lines.grep(/TCP_CORK/).empty?, lines.inspect
-    assert_equal "HI\n", rbuf
+    if defined?(Strace)
+      io, err = Strace.me { @rd.kgio_read(3, rbuf) }
+      assert_nil err
+      lines = io.readlines
+      assert lines.grep(/TCP_CORK/).empty?, lines.inspect
+      assert_equal "HI\n", rbuf
+    else
+      assert_equal "HI\n", @rd.kgio_read(3, rbuf)
+    end
 
     t0 = Time.now
     @rd.kgio_write "HI2U2\n"
     @rd.kgio_write "HOW\n"
     rc = false
-    io, err = Strace.me { rc = @rd.kgio_tryread(666) }
+
+    if defined?(Strace)
+      io, err = Strace.me { rc = @rd.kgio_tryread(666) }
+    else
+      rc = @rd.kgio_tryread(666)
+    end
+
     @wr.readpartial(666, rbuf)
     rbuf == "HI2U2\nHOW\n" or warn "rbuf=#{rbuf.inspect} looking bad?"
     diff = Time.now - t0
     assert(diff < 0.200, "time diff=#{diff} >= 200ms")
     assert_equal :wait_readable, rc
-    assert_nil err
-    lines = io.readlines
-    assert_equal 2, lines.grep(/TCP_CORK/).size, lines.inspect
+    if defined?(Strace)
+      assert_nil err
+      lines = io.readlines
+      assert_equal 2, lines.grep(/TCP_CORK/).size, lines.inspect
+    end
     assert_nothing_raised { @wr.close }
     assert_nothing_raised { @rd.close }
 
     @wr = Kgio::TCPSocket.new(@host, @port)
-    io, err = Strace.me { @rd = @srv.kgio_accept }
-    assert_nil err
-    lines = io.readlines
-    assert lines.grep(/TCP_CORK/).empty?, "optimization fail: #{lines.inspect}"
-    assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0]
-  end if RUBY_PLATFORM =~ /linux/
+    if defined?(Strace)
+      io, err = Strace.me { @rd = @srv.kgio_accept }
+      assert_nil err
+      lines = io.readlines
+      assert lines.grep(/TCP_CORK/).empty?,"optimization fail: #{lines.inspect}"
+      assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0]
+    end
+  end
 
   def teardown
     Kgio.autopush = false
   end
-end
+end if RUBY_PLATFORM =~ /linux|freebsd/