raindrops 0.16.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,46 +1,24 @@
1
1
  #include <ruby.h>
2
2
  #include <stdarg.h>
3
- #ifdef HAVE_RUBY_ST_H
4
- # include <ruby/st.h>
5
- #else
6
- # include <st.h>
7
- #endif
3
+ #include <ruby/st.h>
8
4
  #include "my_fileno.h"
9
5
  #ifdef __linux__
10
6
 
11
- /* Ruby 1.8.6+ macros (for compatibility with Ruby 1.9) */
12
- #ifndef RSTRING_LEN
13
- # define RSTRING_LEN(s) (RSTRING(s)->len)
14
- #endif
15
-
16
- /* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
17
- #if !defined(HAVE_RB_THREAD_BLOCKING_REGION) && \
18
- !defined(HAVE_RB_THREAD_IO_BLOCKING_REGION)
19
- # include <rubysig.h>
20
- # define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
21
- typedef void rb_unblock_function_t(void *);
22
- typedef VALUE rb_blocking_function_t(void *);
23
- static VALUE
24
- rb_thread_blocking_region(
25
- rb_blocking_function_t *func, void *data1,
26
- rb_unblock_function_t *ubf, void *data2)
27
- {
28
- VALUE rv;
29
-
30
- TRAP_BEG;
31
- rv = func(data1);
32
- TRAP_END;
33
-
34
- return rv;
35
- }
36
- #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
37
-
38
7
  #ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION
8
+ /* Ruby 1.9.3 and 2.0.0 */
39
9
  VALUE rb_thread_io_blocking_region(rb_blocking_function_t *, void *, int);
10
+ # define rd_fd_region(fn,data,fd) \
11
+ rb_thread_io_blocking_region((fn),(data),(fd))
12
+ #elif defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && \
13
+ defined(HAVE_RUBY_THREAD_H) && HAVE_RUBY_THREAD_H
14
+ /* in case Ruby 2.0+ ever drops rb_thread_io_blocking_region: */
15
+ # include <ruby/thread.h>
16
+ # define COMPAT_FN (void *(*)(void *))
17
+ # define rd_fd_region(fn,data,fd) \
18
+ rb_thread_call_without_gvl(COMPAT_FN(fn),(data),RUBY_UBF_IO,NULL)
40
19
  #else
41
- # define rb_thread_io_blocking_region(fn,data,fd) \
42
- rb_thread_blocking_region((fn),(data),RUBY_UBF_IO,0)
43
- #endif /* HAVE_RB_THREAD_IO_BLOCKING_REGION */
20
+ # error Ruby <= 1.8 not supported
21
+ #endif
44
22
 
45
23
  #include <assert.h>
46
24
  #include <errno.h>
@@ -214,6 +192,10 @@ static const char *addr_any(sa_family_t family)
214
192
  return ipv6;
215
193
  }
216
194
 
195
+ #ifdef __GNUC__
196
+ static void bug_warn_nogvl(const char *, ...)
197
+ __attribute__((format(printf,1,2)));
198
+ #endif
217
199
  static void bug_warn_nogvl(const char *fmt, ...)
218
200
  {
219
201
  va_list ap;
@@ -223,86 +205,76 @@ static void bug_warn_nogvl(const char *fmt, ...)
223
205
  va_end(ap);
224
206
 
225
207
  fprintf(stderr, "Please report how you produced this at "\
226
- "raindrops-public@bogomips.org\n");
208
+ "raindrops-public@yhbt.net\n");
227
209
  fflush(stderr);
228
210
  }
229
211
 
230
212
  static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
231
213
  {
232
- char *key, *port, *old_key;
214
+ char *host, *key, *port, *old_key;
233
215
  size_t alloca_len;
234
216
  struct listen_stats *stats;
235
- socklen_t keylen;
217
+ socklen_t hostlen;
236
218
  socklen_t portlen = (socklen_t)sizeof("65535");
237
- union any_addr sa;
238
- socklen_t len = sizeof(struct sockaddr_storage);
239
- int rc;
240
- int flags = NI_NUMERICHOST | NI_NUMERICSERV;
219
+ int n;
220
+ const void *src = r->id.idiag_src;
241
221
 
242
- switch ((sa.ss.ss_family = r->idiag_family)) {
222
+ switch (r->idiag_family) {
243
223
  case AF_INET: {
244
- sa.in.sin_port = r->id.idiag_sport;
245
- sa.in.sin_addr.s_addr = r->id.idiag_src[0];
246
- keylen = INET_ADDRSTRLEN;
247
- alloca_len = keylen + 1 + portlen;
248
- key = alloca(alloca_len);
249
- key[keylen] = 0; /* will be ':' later */
250
- port = key + keylen + 1;
251
- rc = getnameinfo(&sa.sa, len,
252
- key, keylen, port, portlen, flags);
224
+ hostlen = INET_ADDRSTRLEN;
225
+ alloca_len = hostlen + portlen;
226
+ host = key = alloca(alloca_len);
253
227
  break;
254
228
  }
255
229
  case AF_INET6: {
256
- sa.in6.sin6_port = r->id.idiag_sport;
257
- memcpy(&sa.in6.sin6_addr, &r->id.idiag_src, sizeof(__be32[4]));
258
- keylen = INET6_ADDRSTRLEN;
259
- /* [ ] */
260
- alloca_len = 1 + keylen + 1 + 1 + portlen;
230
+ hostlen = INET6_ADDRSTRLEN;
231
+ alloca_len = 1 + hostlen + 1 + portlen;
261
232
  key = alloca(alloca_len);
262
- *key = '[';
263
- key[1 + keylen + 1] = 0; /* will be ':' later */
264
- port = 1 + key + keylen + 1 + 1;
265
- rc = getnameinfo(&sa.sa, len,
266
- key + 1, keylen, port, portlen, flags);
233
+ host = key + 1;
267
234
  break;
268
235
  }
269
236
  default:
270
237
  assert(0 && "unsupported address family, could that be IPv7?!");
271
238
  }
272
- if (rc != 0) {
273
- bug_warn_nogvl("BUG: getnameinfo: %s\n", gai_strerror(rc));
274
- *key = 0;
239
+ if (!inet_ntop(r->idiag_family, src, host, hostlen)) {
240
+ bug_warn_nogvl("BUG: inet_ntop: %s\n", strerror(errno));
241
+ *key = '\0';
242
+ *host = '\0';
275
243
  }
276
-
277
- keylen = (socklen_t)strlen(key);
278
- portlen = (socklen_t)strlen(port);
279
-
280
- switch (sa.ss.ss_family) {
244
+ hostlen = (socklen_t)strlen(host);
245
+ switch (r->idiag_family) {
281
246
  case AF_INET:
282
- key[keylen] = ':';
283
- memmove(key + keylen + 1, port, portlen + 1);
247
+ host[hostlen] = ':';
248
+ port = host + hostlen + 1;
284
249
  break;
285
250
  case AF_INET6:
286
- key[keylen] = ']';
287
- key[keylen + 1] = ':';
288
- memmove(key + keylen + 2, port, portlen + 1);
289
- keylen++;
251
+ key[0] = '[';
252
+ host[hostlen] = ']';
253
+ host[hostlen + 1] = ':';
254
+ port = host + hostlen + 2;
290
255
  break;
291
256
  default:
292
257
  assert(0 && "unsupported address family, could that be IPv7?!");
293
258
  }
294
259
 
260
+ n = snprintf(port, portlen, "%u", ntohs(r->id.idiag_sport));
261
+ if (n <= 0) {
262
+ bug_warn_nogvl("BUG: snprintf port: %d\n", n);
263
+ *key = '\0';
264
+ }
265
+
295
266
  if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
296
267
  return stats;
297
268
 
298
269
  old_key = key;
299
270
 
300
271
  if (r->idiag_state == TCP_ESTABLISHED) {
301
- int n = snprintf(key, alloca_len, "%s:%u",
302
- addr_any(sa.ss.ss_family),
272
+ n = snprintf(key, alloca_len, "%s:%u",
273
+ addr_any(r->idiag_family),
303
274
  ntohs(r->id.idiag_sport));
304
275
  if (n <= 0) {
305
276
  bug_warn_nogvl("BUG: snprintf: %d\n", n);
277
+ *key = '\0';
306
278
  }
307
279
  if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
308
280
  return stats;
@@ -315,8 +287,9 @@ static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
315
287
  memcpy(key, old_key, n + 1);
316
288
  }
317
289
  } else {
318
- key = xmalloc(keylen + 1 + portlen + 1);
319
- memcpy(key, old_key, keylen + 1 + portlen + 1);
290
+ size_t old_len = strlen(old_key) + 1;
291
+ key = xmalloc(old_len);
292
+ memcpy(key, old_key, old_len);
320
293
  }
321
294
  stats = xcalloc(1, sizeof(struct listen_stats));
322
295
  st_insert(table, (st_data_t)key, (st_data_t)stats);
@@ -613,7 +586,7 @@ static VALUE tcp_stats(struct nogvl_args *args, VALUE addr)
613
586
  gen_bytecode(&args->iov[2], &query_addr);
614
587
 
615
588
  memset(&args->stats, 0, sizeof(struct listen_stats));
616
- nl_errcheck(rb_thread_io_blocking_region(diag, args, args->fd));
589
+ nl_errcheck(rd_fd_region(diag, args, args->fd));
617
590
 
618
591
  return rb_listen_stats(&args->stats);
619
592
  }
@@ -690,7 +663,7 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
690
663
  "addr must be an array of strings, a string, or nil");
691
664
  }
692
665
 
693
- nl_errcheck(rb_thread_io_blocking_region(diag, &args, args.fd));
666
+ nl_errcheck(rd_fd_region(diag, &args, args.fd));
694
667
 
695
668
  st_foreach(args.table, NIL_P(addrs) ? st_to_hash : st_AND_hash, rv);
696
669
  st_free_table(args.table);
@@ -705,11 +678,12 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
705
678
 
706
679
  void Init_raindrops_linux_inet_diag(void)
707
680
  {
708
- VALUE cRaindrops = rb_const_get(rb_cObject, rb_intern("Raindrops"));
681
+ VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
709
682
  VALUE mLinux = rb_define_module_under(cRaindrops, "Linux");
683
+ VALUE Socket;
710
684
 
711
685
  rb_require("socket");
712
- cIDSock = rb_const_get(rb_cObject, rb_intern("Socket"));
686
+ Socket = rb_const_get(rb_cObject, rb_intern("Socket"));
713
687
  id_new = rb_intern("new");
714
688
 
715
689
  /*
@@ -718,10 +692,11 @@ void Init_raindrops_linux_inet_diag(void)
718
692
  * This is a subclass of +Socket+ specifically for talking
719
693
  * to the inet_diag facility of Netlink.
720
694
  */
721
- cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket", cIDSock);
695
+ cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket", Socket);
722
696
  rb_define_singleton_method(cIDSock, "new", ids_s_new, 0);
723
697
 
724
698
  cListenStats = rb_const_get(cRaindrops, rb_intern("ListenStats"));
699
+ rb_gc_register_mark_object(cListenStats); /* pin */
725
700
 
726
701
  rb_define_module_function(mLinux, "tcp_listener_stats",
727
702
  tcp_listener_stats, -1);
@@ -4,6 +4,7 @@
4
4
  #include <assert.h>
5
5
  #include <errno.h>
6
6
  #include <stddef.h>
7
+ #include <string.h>
7
8
  #include "raindrops_atomic.h"
8
9
 
9
10
  #ifndef SIZET2NUM
@@ -34,11 +35,19 @@ struct raindrops {
34
35
  size_t size;
35
36
  size_t capa;
36
37
  pid_t pid;
38
+ VALUE io;
37
39
  struct raindrop *drops;
38
40
  };
39
41
 
40
42
  /* called by GC */
41
- static void gcfree(void *ptr)
43
+ static void rd_mark(void *ptr)
44
+ {
45
+ struct raindrops *r = ptr;
46
+ rb_gc_mark(r->io);
47
+ }
48
+
49
+ /* called by GC */
50
+ static void rd_free(void *ptr)
42
51
  {
43
52
  struct raindrops *r = ptr;
44
53
 
@@ -51,11 +60,24 @@ static void gcfree(void *ptr)
51
60
  xfree(ptr);
52
61
  }
53
62
 
63
+ static size_t rd_memsize(const void *ptr)
64
+ {
65
+ const struct raindrops *r = ptr;
66
+
67
+ return r->drops == MAP_FAILED ? 0 : raindrop_size * r->capa;
68
+ }
69
+
70
+ static const rb_data_type_t rd_type = {
71
+ "raindrops",
72
+ { rd_mark, rd_free, rd_memsize, /* reserved */ },
73
+ /* parent, data, [ flags ] */
74
+ };
75
+
54
76
  /* automatically called at creation (before initialize) */
55
77
  static VALUE alloc(VALUE klass)
56
78
  {
57
79
  struct raindrops *r;
58
- VALUE rv = Data_Make_Struct(klass, struct raindrops, NULL, gcfree, r);
80
+ VALUE rv = TypedData_Make_Struct(klass, struct raindrops, &rd_type, r);
59
81
 
60
82
  r->drops = MAP_FAILED;
61
83
  return rv;
@@ -65,7 +87,7 @@ static struct raindrops *get(VALUE self)
65
87
  {
66
88
  struct raindrops *r;
67
89
 
68
- Data_Get_Struct(self, struct raindrops, r);
90
+ TypedData_Get_Struct(self, struct raindrops, &rd_type, r);
69
91
 
70
92
  if (r->drops == MAP_FAILED)
71
93
  rb_raise(rb_eStandardError, "invalid or freed Raindrops");
@@ -74,16 +96,10 @@ static struct raindrops *get(VALUE self)
74
96
  }
75
97
 
76
98
  /*
77
- * call-seq:
78
- * Raindrops.new(size) -> raindrops object
79
- *
80
- * Initializes a Raindrops object to hold +size+ counters. +size+ is
81
- * only a hint and the actual number of counters the object has is
82
- * dependent on the CPU model, number of cores, and page size of
83
- * the machine. The actual size of the object will always be equal
84
- * or greater than the specified +size+.
99
+ * This is the _actual_ implementation of #initialize - the Ruby wrapper
100
+ * handles keyword-argument handling then calls this method.
85
101
  */
86
- static VALUE init(VALUE self, VALUE size)
102
+ static VALUE init_cimpl(VALUE self, VALUE size, VALUE io, VALUE zero)
87
103
  {
88
104
  struct raindrops *r = DATA_PTR(self);
89
105
  int tries = 1;
@@ -100,11 +116,23 @@ static VALUE init(VALUE self, VALUE size)
100
116
  r->capa = tmp / raindrop_size;
101
117
  assert(PAGE_ALIGN(raindrop_size * r->capa) == tmp && "not aligned");
102
118
 
119
+ r->io = io;
120
+
103
121
  retry:
104
- r->drops = mmap(NULL, tmp,
105
- PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
122
+ if (RTEST(r->io)) {
123
+ int fd = NUM2INT(rb_funcall(r->io, rb_intern("fileno"), 0));
124
+ rb_funcall(r->io, rb_intern("truncate"), 1, SIZET2NUM(tmp));
125
+ r->drops = mmap(NULL, tmp,
126
+ PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
127
+ } else {
128
+ r->drops = mmap(NULL, tmp,
129
+ PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED,
130
+ -1, 0);
131
+ }
106
132
  if (r->drops == MAP_FAILED) {
107
- if ((errno == EAGAIN || errno == ENOMEM) && tries-- > 0) {
133
+ int err = errno;
134
+
135
+ if ((err == EAGAIN || err == ENOMEM) && tries-- > 0) {
108
136
  rb_gc();
109
137
  goto retry;
110
138
  }
@@ -112,6 +140,9 @@ retry:
112
140
  }
113
141
  r->pid = getpid();
114
142
 
143
+ if (RTEST(zero))
144
+ memset(r->drops, 0, tmp);
145
+
115
146
  return self;
116
147
  }
117
148
 
@@ -140,7 +171,9 @@ static void resize(struct raindrops *r, size_t new_rd_size)
140
171
 
141
172
  rv = mremap(old_address, old_size, new_size, MREMAP_MAYMOVE);
142
173
  if (rv == MAP_FAILED) {
143
- if (errno == EAGAIN || errno == ENOMEM) {
174
+ int err = errno;
175
+
176
+ if (err == EAGAIN || err == ENOMEM) {
144
177
  rb_gc();
145
178
  rv = mremap(old_address, old_size, new_size, 0);
146
179
  }
@@ -200,14 +233,16 @@ static VALUE capa(VALUE self)
200
233
  * call-seq:
201
234
  * rd.dup -> rd_copy
202
235
  *
203
- * Duplicates and snapshots the current state of a Raindrops object.
236
+ * Duplicates and snapshots the current state of a Raindrops object. Even
237
+ * if the given Raindrops object is backed by a file, the copy will be backed
238
+ * by independent, anonymously mapped memory.
204
239
  */
205
240
  static VALUE init_copy(VALUE dest, VALUE source)
206
241
  {
207
242
  struct raindrops *dst = DATA_PTR(dest);
208
243
  struct raindrops *src = get(source);
209
244
 
210
- init(dest, SIZET2NUM(src->size));
245
+ init_cimpl(dest, SIZET2NUM(src->size), Qnil, Qfalse);
211
246
  memcpy(dst->drops, src->drops, raindrop_size * src->size);
212
247
 
213
248
  return dest;
@@ -323,7 +358,9 @@ static VALUE aref(VALUE self, VALUE index)
323
358
 
324
359
  #ifdef __linux__
325
360
  void Init_raindrops_linux_inet_diag(void);
326
- void Init_raindrops_linux_tcp_info(void);
361
+ #endif
362
+ #ifdef HAVE_TYPE_STRUCT_TCP_INFO
363
+ void Init_raindrops_tcp_info(void);
327
364
  #endif
328
365
 
329
366
  #ifndef _SC_NPROCESSORS_CONF
@@ -356,6 +393,20 @@ static VALUE evaporate_bang(VALUE self)
356
393
  return Qnil;
357
394
  }
358
395
 
396
+ /*
397
+ * call-seq:
398
+ * to_io -> IO
399
+ *
400
+ * Returns the IO object backing the memory for this raindrop, if
401
+ * one was specified when constructing this Raindrop. If this
402
+ * Raindrop is backed by anonymous memory, this method returns nil.
403
+ */
404
+ static VALUE to_io(VALUE self)
405
+ {
406
+ struct raindrops *r = get(self);
407
+ return r->io;
408
+ }
409
+
359
410
  void Init_raindrops_ext(void)
360
411
  {
361
412
  VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
@@ -414,7 +465,7 @@ void Init_raindrops_ext(void)
414
465
 
415
466
  rb_define_alloc_func(cRaindrops, alloc);
416
467
 
417
- rb_define_method(cRaindrops, "initialize", init, 1);
468
+ rb_define_private_method(cRaindrops, "initialize_cimpl", init_cimpl, 3);
418
469
  rb_define_method(cRaindrops, "incr", incr, -1);
419
470
  rb_define_method(cRaindrops, "decr", decr, -1);
420
471
  rb_define_method(cRaindrops, "to_ary", to_ary, 0);
@@ -425,9 +476,12 @@ void Init_raindrops_ext(void)
425
476
  rb_define_method(cRaindrops, "capa", capa, 0);
426
477
  rb_define_method(cRaindrops, "initialize_copy", init_copy, 1);
427
478
  rb_define_method(cRaindrops, "evaporate!", evaporate_bang, 0);
479
+ rb_define_method(cRaindrops, "to_io", to_io, 0);
428
480
 
429
481
  #ifdef __linux__
430
482
  Init_raindrops_linux_inet_diag();
431
- Init_raindrops_linux_tcp_info();
483
+ #endif
484
+ #ifdef HAVE_TYPE_STRUCT_TCP_INFO
485
+ Init_raindrops_tcp_info();
432
486
  #endif
433
487
  }
@@ -0,0 +1,245 @@
1
+ #include <ruby.h>
2
+ #include <sys/socket.h>
3
+ #include <netinet/in.h>
4
+ #if defined(HAVE_LINUX_TCP_H)
5
+ # include <linux/tcp.h>
6
+ #else
7
+ # if defined(HAVE_NETINET_TCP_H)
8
+ # include <netinet/tcp.h>
9
+ # endif
10
+ # if defined(HAVE_NETINET_TCP_FSM_H)
11
+ # include <netinet/tcp_fsm.h>
12
+ # endif
13
+ #endif
14
+
15
+ #ifdef HAVE_TYPE_STRUCT_TCP_INFO
16
+ #include "my_fileno.h"
17
+
18
+ CFUNC_tcp_info_tcpi_state
19
+ CFUNC_tcp_info_tcpi_ca_state
20
+ CFUNC_tcp_info_tcpi_retransmits
21
+ CFUNC_tcp_info_tcpi_probes
22
+ CFUNC_tcp_info_tcpi_backoff
23
+ CFUNC_tcp_info_tcpi_options
24
+ CFUNC_tcp_info_tcpi_snd_wscale
25
+ CFUNC_tcp_info_tcpi_rcv_wscale
26
+ CFUNC_tcp_info_tcpi_rto
27
+ CFUNC_tcp_info_tcpi_ato
28
+ CFUNC_tcp_info_tcpi_snd_mss
29
+ CFUNC_tcp_info_tcpi_rcv_mss
30
+ CFUNC_tcp_info_tcpi_unacked
31
+ CFUNC_tcp_info_tcpi_sacked
32
+ CFUNC_tcp_info_tcpi_lost
33
+ CFUNC_tcp_info_tcpi_retrans
34
+ CFUNC_tcp_info_tcpi_fackets
35
+ CFUNC_tcp_info_tcpi_last_data_sent
36
+ CFUNC_tcp_info_tcpi_last_ack_sent
37
+ CFUNC_tcp_info_tcpi_last_data_recv
38
+ CFUNC_tcp_info_tcpi_last_ack_recv
39
+ CFUNC_tcp_info_tcpi_pmtu
40
+ CFUNC_tcp_info_tcpi_rcv_ssthresh
41
+ CFUNC_tcp_info_tcpi_rtt
42
+ CFUNC_tcp_info_tcpi_rttvar
43
+ CFUNC_tcp_info_tcpi_snd_ssthresh
44
+ CFUNC_tcp_info_tcpi_snd_cwnd
45
+ CFUNC_tcp_info_tcpi_advmss
46
+ CFUNC_tcp_info_tcpi_reordering
47
+ CFUNC_tcp_info_tcpi_rcv_rtt
48
+ CFUNC_tcp_info_tcpi_rcv_space
49
+ CFUNC_tcp_info_tcpi_total_retrans
50
+
51
+ static size_t tcpi_memsize(const void *ptr)
52
+ {
53
+ return sizeof(struct tcp_info);
54
+ }
55
+
56
+ static const rb_data_type_t tcpi_type = {
57
+ "tcp_info",
58
+ { NULL, RUBY_TYPED_DEFAULT_FREE, tcpi_memsize, /* reserved */ },
59
+ /* parent, data, [ flags ] */
60
+ };
61
+
62
+ static VALUE alloc(VALUE klass)
63
+ {
64
+ struct tcp_info *info;
65
+
66
+ return TypedData_Make_Struct(klass, struct tcp_info, &tcpi_type, info);
67
+ }
68
+
69
+ /*
70
+ * call-seq:
71
+ *
72
+ * Raindrops::TCP_Info.new(tcp_socket) -> TCP_Info object
73
+ *
74
+ * Reads a TCP_Info object from any given +tcp_socket+. See the tcp(7)
75
+ * manpage and /usr/include/linux/tcp.h for more details.
76
+ */
77
+ static VALUE init(VALUE self, VALUE io)
78
+ {
79
+ int fd = my_fileno(io);
80
+ struct tcp_info *info = DATA_PTR(self);
81
+ socklen_t len = (socklen_t)sizeof(struct tcp_info);
82
+ int rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, info, &len);
83
+
84
+ if (rc != 0)
85
+ rb_sys_fail("getsockopt");
86
+
87
+ return self;
88
+ }
89
+
90
+ void Init_raindrops_tcp_info(void)
91
+ {
92
+ VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
93
+ VALUE cTCP_Info;
94
+
95
+ /*
96
+ * Document-class: Raindrops::TCP_Info
97
+ *
98
+ * This is used to wrap "struct tcp_info" as described in tcp(7)
99
+ * and /usr/include/linux/tcp.h. The following readers methods
100
+ * are defined corresponding to the "tcpi_" fields in the
101
+ * tcp_info struct.
102
+ *
103
+ * As of raindrops 0.18.0+, this is supported on FreeBSD and OpenBSD
104
+ * systems as well as Linux, although not all fields exist or
105
+ * match the documentation, below.
106
+ *
107
+ * In particular, the +last_data_recv+ field is useful for measuring
108
+ * the amount of time a client spent in the listen queue before
109
+ * +accept()+, but only if +TCP_DEFER_ACCEPT+ is used with the
110
+ * listen socket (it is on by default in Unicorn).
111
+ *
112
+ * - state
113
+ * - ca_state
114
+ * - retransmits
115
+ * - probes
116
+ * - backoff
117
+ * - options
118
+ * - snd_wscale
119
+ * - rcv_wscale
120
+ * - rto
121
+ * - ato
122
+ * - snd_mss
123
+ * - rcv_mss
124
+ * - unacked
125
+ * - sacked
126
+ * - lost
127
+ * - retrans
128
+ * - fackets
129
+ * - last_data_sent
130
+ * - last_ack_sent
131
+ * - last_data_recv
132
+ * - last_ack_recv
133
+ * - pmtu
134
+ * - rcv_ssthresh
135
+ * - rtt
136
+ * - rttvar
137
+ * - snd_ssthresh
138
+ * - snd_cwnd
139
+ * - advmss
140
+ * - reordering
141
+ * - rcv_rtt
142
+ * - rcv_space
143
+ * - total_retrans
144
+ *
145
+ * https://kernel.org/doc/man-pages/online/pages/man7/tcp.7.html
146
+ */
147
+ cTCP_Info = rb_define_class_under(cRaindrops, "TCP_Info", rb_cObject);
148
+ rb_define_alloc_func(cTCP_Info, alloc);
149
+ rb_define_private_method(cTCP_Info, "initialize", init, 1);
150
+
151
+ /*
152
+ * Document-method: Raindrops::TCP_Info#get!
153
+ *
154
+ * call-seq:
155
+ *
156
+ * info = Raindrops::TCP_Info.new(tcp_socket)
157
+ * info.get!(tcp_socket)
158
+ *
159
+ * Update an existing TCP_Info objects with the latest stats
160
+ * from the given socket. This even allows sharing TCP_Info
161
+ * objects between different sockets to avoid garbage.
162
+ */
163
+ rb_define_method(cTCP_Info, "get!", init, 1);
164
+
165
+ DEFINE_METHOD_tcp_info_tcpi_state;
166
+ DEFINE_METHOD_tcp_info_tcpi_ca_state;
167
+ DEFINE_METHOD_tcp_info_tcpi_retransmits;
168
+ DEFINE_METHOD_tcp_info_tcpi_probes;
169
+ DEFINE_METHOD_tcp_info_tcpi_backoff;
170
+ DEFINE_METHOD_tcp_info_tcpi_options;
171
+ DEFINE_METHOD_tcp_info_tcpi_snd_wscale;
172
+ DEFINE_METHOD_tcp_info_tcpi_rcv_wscale;
173
+ DEFINE_METHOD_tcp_info_tcpi_rto;
174
+ DEFINE_METHOD_tcp_info_tcpi_ato;
175
+ DEFINE_METHOD_tcp_info_tcpi_snd_mss;
176
+ DEFINE_METHOD_tcp_info_tcpi_rcv_mss;
177
+ DEFINE_METHOD_tcp_info_tcpi_unacked;
178
+ DEFINE_METHOD_tcp_info_tcpi_sacked;
179
+ DEFINE_METHOD_tcp_info_tcpi_lost;
180
+ DEFINE_METHOD_tcp_info_tcpi_retrans;
181
+ DEFINE_METHOD_tcp_info_tcpi_fackets;
182
+ DEFINE_METHOD_tcp_info_tcpi_last_data_sent;
183
+ DEFINE_METHOD_tcp_info_tcpi_last_ack_sent;
184
+ DEFINE_METHOD_tcp_info_tcpi_last_data_recv;
185
+ DEFINE_METHOD_tcp_info_tcpi_last_ack_recv;
186
+ DEFINE_METHOD_tcp_info_tcpi_pmtu;
187
+ DEFINE_METHOD_tcp_info_tcpi_rcv_ssthresh;
188
+ DEFINE_METHOD_tcp_info_tcpi_rtt;
189
+ DEFINE_METHOD_tcp_info_tcpi_rttvar;
190
+ DEFINE_METHOD_tcp_info_tcpi_snd_ssthresh;
191
+ DEFINE_METHOD_tcp_info_tcpi_snd_cwnd;
192
+ DEFINE_METHOD_tcp_info_tcpi_advmss;
193
+ DEFINE_METHOD_tcp_info_tcpi_reordering;
194
+ DEFINE_METHOD_tcp_info_tcpi_rcv_rtt;
195
+ DEFINE_METHOD_tcp_info_tcpi_rcv_space;
196
+ DEFINE_METHOD_tcp_info_tcpi_total_retrans;
197
+
198
+ #ifdef RAINDROPS_TCP_STATES_ALL_KNOWN
199
+
200
+ /*
201
+ * Document-const: Raindrops::TCP
202
+ *
203
+ * This is a frozen hash storing the numeric values
204
+ * maps platform-independent symbol keys to
205
+ * platform-dependent numeric values. These states
206
+ * are all valid values for the Raindrops::TCP_Info#state field.
207
+ *
208
+ * The platform-independent names of the keys in this hash are:
209
+ *
210
+ * - :ESTABLISHED
211
+ * - :SYN_SENT
212
+ * - :SYN_RECV
213
+ * - :FIN_WAIT1
214
+ * - :FIN_WAIT2
215
+ * - :TIME_WAIT
216
+ * - :CLOSE
217
+ * - :CLOSE_WAIT
218
+ * - :LAST_ACK
219
+ * - :LISTEN
220
+ * - :CLOSING
221
+ *
222
+ * This is only supported on platforms where TCP_Info is supported,
223
+ * currently FreeBSD, OpenBSD, and Linux-based systems.
224
+ */
225
+ {
226
+ #define TCPSET(n,v) rb_hash_aset(tcp, ID2SYM(rb_intern(#n)), INT2NUM(v))
227
+ VALUE tcp = rb_hash_new();
228
+ TCPSET(ESTABLISHED, RAINDROPS_TCP_ESTABLISHED);
229
+ TCPSET(SYN_SENT, RAINDROPS_TCP_SYN_SENT);
230
+ TCPSET(SYN_RECV, RAINDROPS_TCP_SYN_RECV);
231
+ TCPSET(FIN_WAIT1, RAINDROPS_TCP_FIN_WAIT1);
232
+ TCPSET(FIN_WAIT2, RAINDROPS_TCP_FIN_WAIT2);
233
+ TCPSET(TIME_WAIT, RAINDROPS_TCP_TIME_WAIT);
234
+ TCPSET(CLOSE, RAINDROPS_TCP_CLOSE);
235
+ TCPSET(CLOSE_WAIT, RAINDROPS_TCP_CLOSE_WAIT);
236
+ TCPSET(LAST_ACK, RAINDROPS_TCP_LAST_ACK);
237
+ TCPSET(LISTEN, RAINDROPS_TCP_LISTEN);
238
+ TCPSET(CLOSING, RAINDROPS_TCP_CLOSING);
239
+ #undef TCPSET
240
+ OBJ_FREEZE(tcp);
241
+ rb_define_const(cRaindrops, "TCP", tcp);
242
+ }
243
+ #endif
244
+ }
245
+ #endif /* HAVE_STRUCT_TCP_INFO */