diff options
author | Eric Wong <normalperson@yhbt.net> | 2011-01-31 15:18:33 -0800 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2011-01-31 15:32:10 -0800 |
commit | 6479b6d3934b8930910e0057f516aa019dd7a8c7 (patch) | |
tree | 625b34639cc1819cde2e0f013637efcda161f219 | |
parent | 15744a90cda72e9007914cd2a78b0b2949193479 (diff) | |
download | kgio-6479b6d3934b8930910e0057f516aa019dd7a8c7.tar.gz |
Hopefully it works for people who use TCP_NOPUSH...
-rw-r--r-- | ext/kgio/autopush.c | 36 | ||||
-rw-r--r-- | test/test_autopush.rb | 119 |
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/ |