raindrops RubyGem user+dev discussion/patches/pulls/bugs/help
 help / Atom feed
* [PATCH 0/2] support TCP_INFO under FreeBSD
@ 2017-03-16  3:16 Eric Wong
  2017-03-16  3:16 ` [PATCH 1/2] tcp_info: support this struct " Eric Wong
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Eric Wong @ 2017-03-16  3:16 UTC (permalink / raw)
  To: raindrops-public

FreeBSD seems to have useful enough TCP_INFO support for
checking TCP states and such.

Eric Wong (2):
      tcp_info: support this struct under FreeBSD
      define Raindrops::TCP hash for TCP states

 ext/raindrops/extconf.rb                          | 105 ++++++++++-
 ext/raindrops/linux_tcp_info.c                    | 196 --------------------
 ext/raindrops/raindrops.c                         |   8 +-
 ext/raindrops/tcp_info.c                          | 216 ++++++++++++++++++++++
 test/{test_linux_tcp_info.rb => test_tcp_info.rb} |  40 +++-
 5 files changed, 356 insertions(+), 209 deletions(-)


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

* [PATCH 1/2] tcp_info: support this struct under FreeBSD
  2017-03-16  3:16 [PATCH 0/2] support TCP_INFO under FreeBSD Eric Wong
@ 2017-03-16  3:16 ` " Eric Wong
  2017-03-16  3:16 ` [PATCH 2/2] define Raindrops::TCP hash for TCP states Eric Wong
  2017-03-21  2:55 ` [PATCH (ccc)] http_request: support proposed Raindrops::TCP states on Eric Wong
  2 siblings, 0 replies; 8+ messages in thread
From: Eric Wong @ 2017-03-16  3:16 UTC (permalink / raw)
  To: raindrops-public

Of course these fields are not portable between Linux and FreeBSD,
but they should remain ABI-compatible for future versions of each OS.

Tested on FreeBSD 10.3-RELEASE i386

TCP state names will be another problem...
---
 ext/raindrops/extconf.rb                          |  78 ++++++++++-
 ext/raindrops/raindrops.c                         |   8 +-
 ext/raindrops/{linux_tcp_info.c => tcp_info.c}    | 159 +++++++++++-----------
 test/{test_linux_tcp_info.rb => test_tcp_info.rb} |  37 +++--
 4 files changed, 189 insertions(+), 93 deletions(-)
 rename ext/raindrops/{linux_tcp_info.c => tcp_info.c} (51%)
 rename test/{test_linux_tcp_info.rb => test_tcp_info.rb} (66%)

diff --git a/ext/raindrops/extconf.rb b/ext/raindrops/extconf.rb
index 79d212c..5273b74 100644
--- a/ext/raindrops/extconf.rb
+++ b/ext/raindrops/extconf.rb
@@ -1,4 +1,5 @@
 require 'mkmf'
+require 'shellwords'
 
 dir_config('atomic_ops')
 have_func('mmap', 'sys/mman.h') or abort 'mmap() not found'
@@ -6,9 +7,83 @@
 
 $CPPFLAGS += " -D_GNU_SOURCE "
 have_func('mremap', 'sys/mman.h')
-have_header('linux/tcp.h')
+headers = %w(sys/types.h netdb.h string.h sys/socket.h netinet/in.h)
+if have_header('linux/tcp.h')
+  headers << 'linux/tcp.h'
+else
+  %w(netinet/tcp.h netinet/tcp_fsm.h).each { |h|
+    have_header(h, headers) and headers << h
+  }
+end
 
 $CPPFLAGS += " -D_BSD_SOURCE "
+
+if have_type("struct tcp_info", headers)
+  %w(
+    tcpi_state
+    tcpi_ca_state
+    tcpi_retransmits
+    tcpi_probes
+    tcpi_backoff
+    tcpi_options
+    tcpi_snd_wscale
+    tcpi_rcv_wscale
+    tcpi_rto
+    tcpi_ato
+    tcpi_snd_mss
+    tcpi_rcv_mss
+    tcpi_unacked
+    tcpi_sacked
+    tcpi_lost
+    tcpi_retrans
+    tcpi_fackets
+    tcpi_last_data_sent
+    tcpi_last_ack_sent
+    tcpi_last_data_recv
+    tcpi_last_ack_recv
+    tcpi_pmtu
+    tcpi_rcv_ssthresh
+    tcpi_rtt
+    tcpi_rttvar
+    tcpi_snd_ssthresh
+    tcpi_snd_cwnd
+    tcpi_advmss
+    tcpi_reordering
+    tcpi_rcv_rtt
+    tcpi_rcv_space
+    tcpi_total_retrans
+    tcpi_snd_wnd
+    tcpi_snd_bwnd
+    tcpi_snd_nxt
+    tcpi_rcv_nxt
+    tcpi_toe_tid
+    tcpi_snd_rexmitpack
+    tcpi_rcv_ooopack
+    tcpi_snd_zerowin
+  ).each do |field|
+    cfunc = "tcp_info_#{field}"
+    if have_struct_member('struct tcp_info', field, headers)
+      func_body = <<EOF
+static VALUE #{cfunc}(VALUE self)
+{
+	struct tcp_info *info = DATA_PTR(self);
+	return UINT2NUM((uint32_t)info->#{field});
+}
+EOF
+      func_body.delete!("\n")
+      $defs << "-DCFUNC_#{cfunc}=#{Shellwords.shellescape(func_body)}"
+    else
+      func_body = "static inline void #{cfunc}(void) {}"
+      $defs << "-DCFUNC_#{cfunc}=#{Shellwords.shellescape(func_body)}"
+      cfunc = 'rb_f_notimplement'.freeze
+    end
+    rbmethod = %Q("#{field.sub(/\Atcpi_/, ''.freeze)}")
+    $defs << "-DDEFINE_METHOD_tcp_info_#{field}=" \
+	     "#{Shellwords.shellescape(
+                %Q[rb_define_method(cTCP_Info,#{rbmethod},#{cfunc},0)])}"
+  end
+end
+
 have_func("getpagesize", "unistd.h")
 have_func('rb_thread_blocking_region')
 have_func('rb_thread_io_blocking_region')
@@ -53,4 +128,5 @@
 
   apt-get install libatomic-ops-dev
 SRC
+create_header # generate extconf.h to avoid excessively long command-line
 create_makefile('raindrops_ext')
diff --git a/ext/raindrops/raindrops.c b/ext/raindrops/raindrops.c
index 390b8b8..0ea3e32 100644
--- a/ext/raindrops/raindrops.c
+++ b/ext/raindrops/raindrops.c
@@ -336,7 +336,9 @@ static VALUE aref(VALUE self, VALUE index)
 
 #ifdef __linux__
 void Init_raindrops_linux_inet_diag(void);
-void Init_raindrops_linux_tcp_info(void);
+#endif
+#ifdef HAVE_TYPE_STRUCT_TCP_INFO
+void Init_raindrops_tcp_info(void);
 #endif
 
 #ifndef _SC_NPROCESSORS_CONF
@@ -441,6 +443,8 @@ void Init_raindrops_ext(void)
 
 #ifdef __linux__
 	Init_raindrops_linux_inet_diag();
-	Init_raindrops_linux_tcp_info();
+#endif
+#ifdef HAVE_TYPE_STRUCT_TCP_INFO
+	Init_raindrops_tcp_info();
 #endif
 }
diff --git a/ext/raindrops/linux_tcp_info.c b/ext/raindrops/tcp_info.c
similarity index 51%
rename from ext/raindrops/linux_tcp_info.c
rename to ext/raindrops/tcp_info.c
index 83001a5..dc615f7 100644
--- a/ext/raindrops/linux_tcp_info.c
+++ b/ext/raindrops/tcp_info.c
@@ -1,50 +1,53 @@
-#if defined(__linux__) && defined(HAVE_LINUX_TCP_H)
 #include <ruby.h>
+#include "extconf.h"
 #include <sys/socket.h>
 #include <netinet/in.h>
-#include <linux/tcp.h>
-#ifdef TCP_INFO
-#include "my_fileno.h"
+#if defined(HAVE_LINUX_TCP_H)
+#  include <linux/tcp.h>
+#else
+#  if defined(HAVE_NETINET_TCP_H)
+#    include <netinet/tcp.h>
+#  endif
+#  if defined(HAVE_NETINET_TCP_FSM_H)
+#    include <netinet/tcp_fsm.h>
+#  endif
+#endif
 
-#define TCPI_ATTR_READER(x) \
-static VALUE tcp_info_##x(VALUE self) \
-{ \
-	struct tcp_info *info = DATA_PTR(self); \
-	return UINT2NUM((uint32_t)info->tcpi_##x); \
-}
+#ifdef HAVE_TYPE_STRUCT_TCP_INFO
+#include "my_fileno.h"
 
-TCPI_ATTR_READER(state)
-TCPI_ATTR_READER(ca_state)
-TCPI_ATTR_READER(retransmits)
-TCPI_ATTR_READER(probes)
-TCPI_ATTR_READER(backoff)
-TCPI_ATTR_READER(options)
-TCPI_ATTR_READER(snd_wscale)
-TCPI_ATTR_READER(rcv_wscale)
-TCPI_ATTR_READER(rto)
-TCPI_ATTR_READER(ato)
-TCPI_ATTR_READER(snd_mss)
-TCPI_ATTR_READER(rcv_mss)
-TCPI_ATTR_READER(unacked)
-TCPI_ATTR_READER(sacked)
-TCPI_ATTR_READER(lost)
-TCPI_ATTR_READER(retrans)
-TCPI_ATTR_READER(fackets)
-TCPI_ATTR_READER(last_data_sent)
-TCPI_ATTR_READER(last_ack_sent)
-TCPI_ATTR_READER(last_data_recv)
-TCPI_ATTR_READER(last_ack_recv)
-TCPI_ATTR_READER(pmtu)
-TCPI_ATTR_READER(rcv_ssthresh)
-TCPI_ATTR_READER(rtt)
-TCPI_ATTR_READER(rttvar)
-TCPI_ATTR_READER(snd_ssthresh)
-TCPI_ATTR_READER(snd_cwnd)
-TCPI_ATTR_READER(advmss)
-TCPI_ATTR_READER(reordering)
-TCPI_ATTR_READER(rcv_rtt)
-TCPI_ATTR_READER(rcv_space)
-TCPI_ATTR_READER(total_retrans)
+CFUNC_tcp_info_tcpi_state
+CFUNC_tcp_info_tcpi_ca_state
+CFUNC_tcp_info_tcpi_retransmits
+CFUNC_tcp_info_tcpi_probes
+CFUNC_tcp_info_tcpi_backoff
+CFUNC_tcp_info_tcpi_options
+CFUNC_tcp_info_tcpi_snd_wscale
+CFUNC_tcp_info_tcpi_rcv_wscale
+CFUNC_tcp_info_tcpi_rto
+CFUNC_tcp_info_tcpi_ato
+CFUNC_tcp_info_tcpi_snd_mss
+CFUNC_tcp_info_tcpi_rcv_mss
+CFUNC_tcp_info_tcpi_unacked
+CFUNC_tcp_info_tcpi_sacked
+CFUNC_tcp_info_tcpi_lost
+CFUNC_tcp_info_tcpi_retrans
+CFUNC_tcp_info_tcpi_fackets
+CFUNC_tcp_info_tcpi_last_data_sent
+CFUNC_tcp_info_tcpi_last_ack_sent
+CFUNC_tcp_info_tcpi_last_data_recv
+CFUNC_tcp_info_tcpi_last_ack_recv
+CFUNC_tcp_info_tcpi_pmtu
+CFUNC_tcp_info_tcpi_rcv_ssthresh
+CFUNC_tcp_info_tcpi_rtt
+CFUNC_tcp_info_tcpi_rttvar
+CFUNC_tcp_info_tcpi_snd_ssthresh
+CFUNC_tcp_info_tcpi_snd_cwnd
+CFUNC_tcp_info_tcpi_advmss
+CFUNC_tcp_info_tcpi_reordering
+CFUNC_tcp_info_tcpi_rcv_rtt
+CFUNC_tcp_info_tcpi_rcv_space
+CFUNC_tcp_info_tcpi_total_retrans
 
 static size_t tcpi_memsize(const void *ptr)
 {
@@ -85,7 +88,7 @@ static VALUE init(VALUE self, VALUE io)
 	return self;
 }
 
-void Init_raindrops_linux_tcp_info(void)
+void Init_raindrops_tcp_info(void)
 {
 	VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
 	VALUE cTCP_Info;
@@ -156,41 +159,37 @@ void Init_raindrops_linux_tcp_info(void)
 	 */
 	rb_define_method(cTCP_Info, "get!", init, 1);
 
-#define TCPI_DEFINE_METHOD(x) \
-	rb_define_method(cTCP_Info, #x, tcp_info_##x, 0)
-
-	TCPI_DEFINE_METHOD(state);
-	TCPI_DEFINE_METHOD(ca_state);
-	TCPI_DEFINE_METHOD(retransmits);
-	TCPI_DEFINE_METHOD(probes);
-	TCPI_DEFINE_METHOD(backoff);
-	TCPI_DEFINE_METHOD(options);
-	TCPI_DEFINE_METHOD(snd_wscale);
-	TCPI_DEFINE_METHOD(rcv_wscale);
-	TCPI_DEFINE_METHOD(rto);
-	TCPI_DEFINE_METHOD(ato);
-	TCPI_DEFINE_METHOD(snd_mss);
-	TCPI_DEFINE_METHOD(rcv_mss);
-	TCPI_DEFINE_METHOD(unacked);
-	TCPI_DEFINE_METHOD(sacked);
-	TCPI_DEFINE_METHOD(lost);
-	TCPI_DEFINE_METHOD(retrans);
-	TCPI_DEFINE_METHOD(fackets);
-	TCPI_DEFINE_METHOD(last_data_sent);
-	TCPI_DEFINE_METHOD(last_ack_sent);
-	TCPI_DEFINE_METHOD(last_data_recv);
-	TCPI_DEFINE_METHOD(last_ack_recv);
-	TCPI_DEFINE_METHOD(pmtu);
-	TCPI_DEFINE_METHOD(rcv_ssthresh);
-	TCPI_DEFINE_METHOD(rtt);
-	TCPI_DEFINE_METHOD(rttvar);
-	TCPI_DEFINE_METHOD(snd_ssthresh);
-	TCPI_DEFINE_METHOD(snd_cwnd);
-	TCPI_DEFINE_METHOD(advmss);
-	TCPI_DEFINE_METHOD(reordering);
-	TCPI_DEFINE_METHOD(rcv_rtt);
-	TCPI_DEFINE_METHOD(rcv_space);
-	TCPI_DEFINE_METHOD(total_retrans);
+	DEFINE_METHOD_tcp_info_tcpi_state;
+	DEFINE_METHOD_tcp_info_tcpi_ca_state;
+	DEFINE_METHOD_tcp_info_tcpi_retransmits;
+	DEFINE_METHOD_tcp_info_tcpi_probes;
+	DEFINE_METHOD_tcp_info_tcpi_backoff;
+	DEFINE_METHOD_tcp_info_tcpi_options;
+	DEFINE_METHOD_tcp_info_tcpi_snd_wscale;
+	DEFINE_METHOD_tcp_info_tcpi_rcv_wscale;
+	DEFINE_METHOD_tcp_info_tcpi_rto;
+	DEFINE_METHOD_tcp_info_tcpi_ato;
+	DEFINE_METHOD_tcp_info_tcpi_snd_mss;
+	DEFINE_METHOD_tcp_info_tcpi_rcv_mss;
+	DEFINE_METHOD_tcp_info_tcpi_unacked;
+	DEFINE_METHOD_tcp_info_tcpi_sacked;
+	DEFINE_METHOD_tcp_info_tcpi_lost;
+	DEFINE_METHOD_tcp_info_tcpi_retrans;
+	DEFINE_METHOD_tcp_info_tcpi_fackets;
+	DEFINE_METHOD_tcp_info_tcpi_last_data_sent;
+	DEFINE_METHOD_tcp_info_tcpi_last_ack_sent;
+	DEFINE_METHOD_tcp_info_tcpi_last_data_recv;
+	DEFINE_METHOD_tcp_info_tcpi_last_ack_recv;
+	DEFINE_METHOD_tcp_info_tcpi_pmtu;
+	DEFINE_METHOD_tcp_info_tcpi_rcv_ssthresh;
+	DEFINE_METHOD_tcp_info_tcpi_rtt;
+	DEFINE_METHOD_tcp_info_tcpi_rttvar;
+	DEFINE_METHOD_tcp_info_tcpi_snd_ssthresh;
+	DEFINE_METHOD_tcp_info_tcpi_snd_cwnd;
+	DEFINE_METHOD_tcp_info_tcpi_advmss;
+	DEFINE_METHOD_tcp_info_tcpi_reordering;
+	DEFINE_METHOD_tcp_info_tcpi_rcv_rtt;
+	DEFINE_METHOD_tcp_info_tcpi_rcv_space;
+	DEFINE_METHOD_tcp_info_tcpi_total_retrans;
 }
-#endif /* TCP_INFO */
-#endif /* defined(__linux__) && defined(HAVE_LINUX_TCP_H) */
+#endif /* HAVE_STRUCT_TCP_INFO */
diff --git a/test/test_linux_tcp_info.rb b/test/test_tcp_info.rb
similarity index 66%
rename from test/test_linux_tcp_info.rb
rename to test/test_tcp_info.rb
index c947211..15df087 100644
--- a/test/test_linux_tcp_info.rb
+++ b/test/test_tcp_info.rb
@@ -5,15 +5,15 @@
 require 'socket'
 require 'pp'
 $stderr.sync = $stdout.sync = true
-class TestLinuxTCP_Info < Test::Unit::TestCase
+class TestTCP_Info < Test::Unit::TestCase
 
   TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
 
   # Linux kernel commit 5ee3afba88f5a79d0bff07ddd87af45919259f91
   TCP_INFO_useful_listenq = `uname -r`.strip >= '2.6.24'
 
-
-  def test_tcp_server
+  def test_tcp_server_unacked
+    return if RUBY_PLATFORM !~ /linux/ # unacked not implemented on others...
     s = TCPServer.new(TEST_ADDR, 0)
     rv = Raindrops::TCP_Info.new s
     c = TCPSocket.new TEST_ADDR, s.addr[1]
@@ -29,10 +29,8 @@ def test_tcp_server
     tmp.get!(s)
     assert_equal before, tmp.object_id
 
-    ensure
-      c.close if c
-      a.close if a
-      s.close
+  ensure
+    [ c, a, s ].compact.each(&:close)
   end
 
   def test_accessors
@@ -42,12 +40,14 @@ def test_accessors
     assert tcp_info_methods.size >= 32
     tcp_info_methods.each do |m|
       next if m.to_sym == :get!
+      next if ! tmp.respond_to?(m)
       val = tmp.__send__ m
       assert_kind_of Integer, val
       assert val >= 0
     end
-    ensure
-      s.close
+    assert tmp.respond_to?(:state), 'every OS knows about TCP state, right?'
+  ensure
+    s.close
   end
 
   def test_tcp_server_delayed
@@ -65,4 +65,21 @@ def test_tcp_server_delayed
       a.close if a
       s.close
   end
-end if RUBY_PLATFORM =~ /linux/
+
+  def test_tcp_server_state_closed
+    s = TCPServer.new(TEST_ADDR, 0)
+    c = TCPSocket.new(TEST_ADDR, s.addr[1])
+    i = Raindrops::TCP_Info.allocate
+    a = s.accept
+    i.get!(a)
+    state = i.state
+    c = c.close
+    sleep(0.01) # wait for kernel to update state
+    i.get!(a)
+    assert_not_equal state, i.state
+  ensure
+    s.close if s
+    c.close if c
+    a.close if a
+  end
+end if defined? Raindrops::TCP_Info
-- 
EW


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

* [PATCH 2/2] define Raindrops::TCP hash for TCP states
  2017-03-16  3:16 [PATCH 0/2] support TCP_INFO under FreeBSD Eric Wong
  2017-03-16  3:16 ` [PATCH 1/2] tcp_info: support this struct " Eric Wong
@ 2017-03-16  3:16 ` Eric Wong
  2017-03-21  2:55 ` [PATCH (ccc)] http_request: support proposed Raindrops::TCP states on Eric Wong
  2 siblings, 0 replies; 8+ messages in thread
From: Eric Wong @ 2017-03-16  3:16 UTC (permalink / raw)
  To: raindrops-public

FreeBSD not only uses different values than Linux for TCP
states, but different names, too.  To ease writing portable code
between the OSes, do more CPP metaprogramming via extconf.rb
and define a common hash supported on both OSes.

Putting all this in a hash allows for easy dumping and mapping
in an OS-neutral way, since the actual TCP states are
OS-independent.
---
 ext/raindrops/extconf.rb | 27 +++++++++++++++++++++++++++
 ext/raindrops/tcp_info.c | 21 +++++++++++++++++++++
 test/test_tcp_info.rb    |  3 +++
 3 files changed, 51 insertions(+)

diff --git a/ext/raindrops/extconf.rb b/ext/raindrops/extconf.rb
index 5273b74..86c7d78 100644
--- a/ext/raindrops/extconf.rb
+++ b/ext/raindrops/extconf.rb
@@ -82,6 +82,33 @@
 	     "#{Shellwords.shellescape(
                 %Q[rb_define_method(cTCP_Info,#{rbmethod},#{cfunc},0)])}"
   end
+  tcp_state_map = {
+    ESTABLISHED: %w(TCP_ESTABLISHED TCPS_ESTABLISHED),
+    SYN_SENT: %w(TCP_SYN_SENT TCPS_SYN_SENT),
+    SYN_RECV: %w(TCP_SYN_RECV TCPS_SYN_RECEIVED),
+    FIN_WAIT1: %w(TCP_FIN_WAIT1 TCPS_FIN_WAIT_1),
+    FIN_WAIT2: %w(TCP_FIN_WAIT2 TCPS_FIN_WAIT_2),
+    TIME_WAIT: %w(TCP_TIME_WAIT TCPS_TIME_WAIT),
+    CLOSE: %w(TCP_CLOSE TCPS_CLOSED),
+    CLOSE_WAIT: %w(TCP_CLOSE_WAIT TCPS_CLOSE_WAIT),
+    LAST_ACK: %w(TCP_LAST_ACK TCPS_LAST_ACK),
+    LISTEN: %w(TCP_LISTEN TCPS_LISTEN),
+    CLOSING: %w(TCP_CLOSING TCPS_CLOSING),
+  }
+  nstate = 0
+  tcp_state_map.each do |state, try|
+    try.each do |os_name|
+      have_const(os_name, headers) or next
+      tcp_state_map[state] = os_name
+      nstate += 1
+    end
+  end
+  if nstate == tcp_state_map.size
+    $defs << '-DRAINDROPS_TCP_STATES_ALL_KNOWN=1'
+    tcp_state_map.each do |state, name|
+      $defs << "-DRAINDROPS_TCP_#{state}=#{name}"
+    end
+  end
 end
 
 have_func("getpagesize", "unistd.h")
diff --git a/ext/raindrops/tcp_info.c b/ext/raindrops/tcp_info.c
index dc615f7..3e241a1 100644
--- a/ext/raindrops/tcp_info.c
+++ b/ext/raindrops/tcp_info.c
@@ -191,5 +191,26 @@ void Init_raindrops_tcp_info(void)
 	DEFINE_METHOD_tcp_info_tcpi_rcv_rtt;
 	DEFINE_METHOD_tcp_info_tcpi_rcv_space;
 	DEFINE_METHOD_tcp_info_tcpi_total_retrans;
+
+#ifdef RAINDROPS_TCP_STATES_ALL_KNOWN
+	{
+#define TCPSET(n,v) rb_hash_aset(tcp, ID2SYM(rb_intern(#n)), INT2NUM(v))
+		VALUE tcp = rb_hash_new();
+		TCPSET(ESTABLISHED, RAINDROPS_TCP_ESTABLISHED);
+		TCPSET(SYN_SENT, RAINDROPS_TCP_SYN_SENT);
+		TCPSET(SYN_RECV, RAINDROPS_TCP_SYN_RECV);
+		TCPSET(FIN_WAIT1, RAINDROPS_TCP_FIN_WAIT1);
+		TCPSET(FIN_WAIT2, RAINDROPS_TCP_FIN_WAIT2);
+		TCPSET(TIME_WAIT, RAINDROPS_TCP_TIME_WAIT);
+		TCPSET(CLOSE, RAINDROPS_TCP_CLOSE);
+		TCPSET(CLOSE_WAIT, RAINDROPS_TCP_CLOSE_WAIT);
+		TCPSET(LAST_ACK, RAINDROPS_TCP_LAST_ACK);
+		TCPSET(LISTEN, RAINDROPS_TCP_LISTEN);
+		TCPSET(CLOSING, RAINDROPS_TCP_CLOSING);
+#undef TCPSET
+		OBJ_FREEZE(tcp);
+		rb_define_const(cRaindrops, "TCP", tcp);
+	}
+#endif
 }
 #endif /* HAVE_STRUCT_TCP_INFO */
diff --git a/test/test_tcp_info.rb b/test/test_tcp_info.rb
index 15df087..b107565 100644
--- a/test/test_tcp_info.rb
+++ b/test/test_tcp_info.rb
@@ -73,6 +73,9 @@ def test_tcp_server_state_closed
     a = s.accept
     i.get!(a)
     state = i.state
+    if Raindrops.const_defined?(:TCP)
+      assert_equal state, Raindrops::TCP[:ESTABLISHED]
+    end
     c = c.close
     sleep(0.01) # wait for kernel to update state
     i.get!(a)
-- 
EW


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

* [PATCH (ccc)] http_request: support proposed Raindrops::TCP states on
  2017-03-16  3:16 [PATCH 0/2] support TCP_INFO under FreeBSD Eric Wong
  2017-03-16  3:16 ` [PATCH 1/2] tcp_info: support this struct " Eric Wong
  2017-03-16  3:16 ` [PATCH 2/2] define Raindrops::TCP hash for TCP states Eric Wong
@ 2017-03-21  2:55 ` Eric Wong
  2017-03-21  7:50   ` Simon Eskildsen
  2017-03-21 18:48   ` Jeremy Evans
  2 siblings, 2 replies; 8+ messages in thread
From: Eric Wong @ 2017-03-21  2:55 UTC (permalink / raw)
  To: Simon Eskildsen, unicorn-public; +Cc: raindrops-public, Jeremy Evans

TCP states names/numbers seem stable for each OS, but differ in
name and numeric values between them.  So I started upon this
patch series for raindrops last week:

  https://bogomips.org/raindrops-public/20170316031652.17433-1-e@80x24.org/T/

And things seem to be alright, for the most part.  Anyways
here's a proposed patch for unicorn which takes advantage of
the above (proposed) changes for raindrops and will allow
unicorn to support check_client_connection

Also Cc:-ing Jeremy to see if he has any input on the OpenBSD
side of things.

This goes on top of commit 20c66dbf1ebd0ca993e7a79c9d0d833d747df358
("http_request: reduce insn size for check_client_connection")
at: git://bogomips.org/unicorn ccc-tcp-v3

-------8<--------
Subject: [PATCH] http_request: support proposed Raindrops::TCP states on
 non-Linux

raindrops 0.18+ will have Raindrops::TCP state hash for portable
mapping of TCP states to their respective numeric values.  This
was necessary because TCP state numbers (and even macro names)
differ between FreeBSD and Linux (and possibly other OSes).

Favor using the Raindrops::TCP state hash if available, but
fall back to the hard-coded values since older versions of
raindrops did not support TCP_INFO on non-Linux systems.

While we're in the area, favor "const_defined?" over "defined?"
to reduce the inline constant cache footprint for branches
which are only evaluated once.

Patches to implement Raindrops::TCP for FreeBSD are available at:

  https://bogomips.org/raindrops-public/20170316031652.17433-1-e@80x24.org/T/
---
 lib/unicorn/http_request.rb | 38 +++++++++++++++++++++++++++++---------
 1 file changed, 29 insertions(+), 9 deletions(-)

diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index 9010007..7253497 100644
--- a/lib/unicorn/http_request.rb
+++ b/lib/unicorn/http_request.rb
@@ -105,7 +105,7 @@ def hijacked?
     env.include?('rack.hijack_io'.freeze)
   end
 
-  if defined?(Raindrops::TCP_Info)
+  if Raindrops.const_defined?(:TCP_Info)
     TCPI = Raindrops::TCP_Info.allocate
 
     def check_client_connection(socket) # :nodoc:
@@ -118,14 +118,34 @@ def check_client_connection(socket) # :nodoc:
       end
     end
 
-    def closed_state?(state) # :nodoc:
-      case state
-      when 1 # ESTABLISHED
-        false
-      when 8, 6, 7, 9, 11 # CLOSE_WAIT, TIME_WAIT, CLOSE, LAST_ACK, CLOSING
-        true
-      else
-        false
+    if Raindrops.const_defined?(:TCP)
+      # raindrops 0.18.0+ supports FreeBSD + Linux using the same names
+      # Evaluate these hash lookups at load time so we can
+      # generate an opt_case_dispatch instruction
+      eval <<-EOS
+      def closed_state?(state) # :nodoc:
+        case state
+        when #{Raindrops::TCP[:ESTABLISHED]}
+          false
+        when #{Raindrops::TCP.values_at(
+              :CLOSE_WAIT, :TIME_WAIT, :CLOSE, :LAST_ACK, :CLOSING).join(',')}
+          true
+        else
+          false
+        end
+      end
+      EOS
+    else
+      # raindrops before 0.18 only supported TCP_INFO under Linux
+      def closed_state?(state) # :nodoc:
+        case state
+        when 1 # ESTABLISHED
+          false
+        when 8, 6, 7, 9, 11 # CLOSE_WAIT, TIME_WAIT, CLOSE, LAST_ACK, CLOSING
+          true
+        else
+          false
+        end
       end
     end
   else
-- 
EW

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

* Re: [PATCH (ccc)] http_request: support proposed Raindrops::TCP states on
  2017-03-21  2:55 ` [PATCH (ccc)] http_request: support proposed Raindrops::TCP states on Eric Wong
@ 2017-03-21  7:50   ` Simon Eskildsen
  2017-03-21  8:19     ` Eric Wong
  2017-03-21 18:48   ` Jeremy Evans
  1 sibling, 1 reply; 8+ messages in thread
From: Simon Eskildsen @ 2017-03-21  7:50 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public, raindrops-public, Jeremy Evans

This looks good to me.

Only question is, why not make Raindrops 0.18+ a requirement to avoid
the Raindrops.const_defined?(:TCP)?

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

* Re: [PATCH (ccc)] http_request: support proposed Raindrops::TCP states on
  2017-03-21  7:50   ` Simon Eskildsen
@ 2017-03-21  8:19     ` Eric Wong
  0 siblings, 0 replies; 8+ messages in thread
From: Eric Wong @ 2017-03-21  8:19 UTC (permalink / raw)
  To: Simon Eskildsen; +Cc: unicorn-public, raindrops-public, Jeremy Evans

Simon Eskildsen <simon.eskildsen@shopify.com> wrote:
> This looks good to me.

Thanks for taking a look.

> Only question is, why not make Raindrops 0.18+ a requirement to avoid
> the Raindrops.const_defined?(:TCP)?

I'd rather have some wiggle room in case problems are found on
either side; so people can rollback one without affecting the
other(*).

It also relaxes things for distro packagers for coordinating
releases; in case there's other dependencies (whether human or
technical) which slow down the process.


(*) We may drop raindrops as a hard requirement for unicorn at
    some point, too.  The intended use case for counter sharing
    across hundredes/thousands of workers on massively multicore
    CPUs never surfaced.

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

* Re: [PATCH (ccc)] http_request: support proposed Raindrops::TCP states on
  2017-03-21  2:55 ` [PATCH (ccc)] http_request: support proposed Raindrops::TCP states on Eric Wong
  2017-03-21  7:50   ` Simon Eskildsen
@ 2017-03-21 18:48   ` Jeremy Evans
  2017-03-21 19:12     ` Eric Wong
  1 sibling, 1 reply; 8+ messages in thread
From: Jeremy Evans @ 2017-03-21 18:48 UTC (permalink / raw)
  To: Eric Wong; +Cc: Simon Eskildsen, unicorn-public, raindrops-public

On 03/21 02:55, Eric Wong wrote:
> TCP states names/numbers seem stable for each OS, but differ in
> name and numeric values between them.  So I started upon this
> patch series for raindrops last week:
> 
>   https://bogomips.org/raindrops-public/20170316031652.17433-1-e@80x24.org/T/
> 
> And things seem to be alright, for the most part.  Anyways
> here's a proposed patch for unicorn which takes advantage of
> the above (proposed) changes for raindrops and will allow
> unicorn to support check_client_connection
> 
> Also Cc:-ing Jeremy to see if he has any input on the OpenBSD
> side of things.

OpenBSD seems to support the constants you are using in raindrops:

#define TCPS_CLOSED             0       /* closed */
#define TCPS_LISTEN             1       /* listening for connection */
#define TCPS_SYN_SENT           2       /* active, have sent syn */
#define TCPS_SYN_RECEIVED       3       /* have sent and received syn */
#define TCPS_ESTABLISHED        4       /* established */
#define TCPS_CLOSE_WAIT         5       /* rcvd fin, waiting for close */
#define TCPS_FIN_WAIT_1         6       /* have closed, sent fin */
#define TCPS_CLOSING            7       /* closed xchd FIN; await ACK */
#define TCPS_LAST_ACK           8       /* had fin and close; await FIN ACK */
#define TCPS_FIN_WAIT_2         9       /* have closed, fin is acked */
#define TCPS_TIME_WAIT          10      /* in 2*msl quiet wait after close */

I'm fine with dropping raindrops as a unicorn dependency and making it
optional if it isn't necessary for correct operation.

Thanks,
Jeremy

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

* Re: [PATCH (ccc)] http_request: support proposed Raindrops::TCP states on
  2017-03-21 18:48   ` Jeremy Evans
@ 2017-03-21 19:12     ` Eric Wong
  0 siblings, 0 replies; 8+ messages in thread
From: Eric Wong @ 2017-03-21 19:12 UTC (permalink / raw)
  To: Jeremy Evans; +Cc: Simon Eskildsen, unicorn-public, raindrops-public

Jeremy Evans <code@jeremyevans.net> wrote:
> OpenBSD seems to support the constants you are using in raindrops:

Thanks; I expected the commonality with FreeBSD; and I expect
NetBSD and DragonflyBSD to be identical, too.

> I'm fine with dropping raindrops as a unicorn dependency and making it
> optional if it isn't necessary for correct operation.

Alright, it might be in a further-off release, though.
(unless you or somebody wants to accelerate it's removal)

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

end of thread, back to index

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-16  3:16 [PATCH 0/2] support TCP_INFO under FreeBSD Eric Wong
2017-03-16  3:16 ` [PATCH 1/2] tcp_info: support this struct " Eric Wong
2017-03-16  3:16 ` [PATCH 2/2] define Raindrops::TCP hash for TCP states Eric Wong
2017-03-21  2:55 ` [PATCH (ccc)] http_request: support proposed Raindrops::TCP states on Eric Wong
2017-03-21  7:50   ` Simon Eskildsen
2017-03-21  8:19     ` Eric Wong
2017-03-21 18:48   ` Jeremy Evans
2017-03-21 19:12     ` Eric Wong

raindrops RubyGem user+dev discussion/patches/pulls/bugs/help

Archives are clonable:
	git clone --mirror https://bogomips.org/raindrops-public
	git clone --mirror http://ou63pmih66umazou.onion/raindrops-public

Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.lang.ruby.raindrops
	nntp://ou63pmih66umazou.onion/inbox.comp.lang.ruby.raindrops

 note: .onion URLs require Tor: https://www.torproject.org/
       or Tor2web: https://www.tor2web.org/

AGPL code for this site: git clone https://public-inbox.org/ public-inbox