raindrops 0.13.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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.document +1 -2
  3. data/.gitattributes +4 -0
  4. data/.gitignore +1 -1
  5. data/.manifest +7 -5
  6. data/.olddoc.yml +16 -0
  7. data/GIT-VERSION-FILE +1 -1
  8. data/GIT-VERSION-GEN +1 -1
  9. data/GNUmakefile +1 -2
  10. data/LATEST +6 -11
  11. data/LICENSE +3 -3
  12. data/NEWS +158 -0
  13. data/README +33 -40
  14. data/TODO +2 -0
  15. data/archive/.gitignore +3 -0
  16. data/archive/slrnpull.conf +4 -0
  17. data/examples/linux-listener-stats.rb +1 -2
  18. data/examples/watcher_demo.ru +1 -1
  19. data/examples/yahns.conf.rb +30 -0
  20. data/examples/zbatery.conf.rb +4 -1
  21. data/ext/raindrops/extconf.rb +107 -2
  22. data/ext/raindrops/linux_inet_diag.c +94 -101
  23. data/ext/raindrops/raindrops.c +75 -21
  24. data/ext/raindrops/tcp_info.c +245 -0
  25. data/lib/raindrops/aggregate/last_data_recv.rb +1 -5
  26. data/lib/raindrops/aggregate/pmq.rb +23 -17
  27. data/lib/raindrops/aggregate.rb +1 -1
  28. data/lib/raindrops/linux.rb +5 -6
  29. data/lib/raindrops/middleware/proxy.rb +2 -2
  30. data/lib/raindrops/middleware.rb +4 -6
  31. data/lib/raindrops/watcher.rb +13 -13
  32. data/lib/raindrops.rb +25 -1
  33. data/pkg.mk +26 -50
  34. data/raindrops.gemspec +14 -21
  35. data/test/ipv6_enabled.rb +4 -4
  36. data/test/test_aggregate_pmq.rb +1 -1
  37. data/test/test_inet_diag_socket.rb +1 -1
  38. data/test/test_last_data_recv_unicorn.rb +1 -1
  39. data/test/test_linux.rb +10 -2
  40. data/test/test_linux_all_tcp_listen_stats_leak.rb +2 -2
  41. data/test/test_linux_ipv6.rb +8 -0
  42. data/test/test_raindrops.rb +43 -1
  43. data/test/{test_linux_tcp_info.rb → test_tcp_info.rb} +34 -14
  44. data/test/test_watcher.rb +15 -10
  45. metadata +59 -171
  46. data/.wrongdoc.yml +0 -6
  47. data/ChangeLog +0 -1920
  48. data/Rakefile +0 -28
  49. data/ext/raindrops/linux_tcp_info.c +0 -173
@@ -1,45 +1,24 @@
1
1
  #include <ruby.h>
2
- #ifdef HAVE_RUBY_ST_H
3
- # include <ruby/st.h>
4
- #else
5
- # include <st.h>
6
- #endif
2
+ #include <stdarg.h>
3
+ #include <ruby/st.h>
7
4
  #include "my_fileno.h"
8
5
  #ifdef __linux__
9
6
 
10
- /* Ruby 1.8.6+ macros (for compatibility with Ruby 1.9) */
11
- #ifndef RSTRING_LEN
12
- # define RSTRING_LEN(s) (RSTRING(s)->len)
13
- #endif
14
-
15
- /* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
16
- #if !defined(HAVE_RB_THREAD_BLOCKING_REGION) && \
17
- !defined(HAVE_RB_THREAD_IO_BLOCKING_REGION)
18
- # include <rubysig.h>
19
- # define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
20
- typedef void rb_unblock_function_t(void *);
21
- typedef VALUE rb_blocking_function_t(void *);
22
- static VALUE
23
- rb_thread_blocking_region(
24
- rb_blocking_function_t *func, void *data1,
25
- rb_unblock_function_t *ubf, void *data2)
26
- {
27
- VALUE rv;
28
-
29
- TRAP_BEG;
30
- rv = func(data1);
31
- TRAP_END;
32
-
33
- return rv;
34
- }
35
- #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
36
-
37
7
  #ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION
8
+ /* Ruby 1.9.3 and 2.0.0 */
38
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)
39
19
  #else
40
- # define rb_thread_io_blocking_region(fn,data,fd) \
41
- rb_thread_blocking_region((fn),(data),RUBY_UBF_IO,0)
42
- #endif /* HAVE_RB_THREAD_IO_BLOCKING_REGION */
20
+ # error Ruby <= 1.8 not supported
21
+ #endif
43
22
 
44
23
  #include <assert.h>
45
24
  #include <errno.h>
@@ -213,91 +192,89 @@ static const char *addr_any(sa_family_t family)
213
192
  return ipv6;
214
193
  }
215
194
 
216
- static void bug_warn(void)
195
+ #ifdef __GNUC__
196
+ static void bug_warn_nogvl(const char *, ...)
197
+ __attribute__((format(printf,1,2)));
198
+ #endif
199
+ static void bug_warn_nogvl(const char *fmt, ...)
217
200
  {
201
+ va_list ap;
202
+
203
+ va_start(ap, fmt);
204
+ vfprintf(stderr, fmt, ap);
205
+ va_end(ap);
206
+
218
207
  fprintf(stderr, "Please report how you produced this at "\
219
- "raindrops@librelist.org\n");
208
+ "raindrops-public@yhbt.net\n");
220
209
  fflush(stderr);
221
210
  }
222
211
 
223
212
  static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
224
213
  {
225
- char *key, *port, *old_key;
214
+ char *host, *key, *port, *old_key;
226
215
  size_t alloca_len;
227
216
  struct listen_stats *stats;
228
- size_t keylen;
229
- size_t portlen = sizeof("65535");
230
- union any_addr sa;
231
- socklen_t len = sizeof(struct sockaddr_storage);
232
- int rc;
233
- int flags = NI_NUMERICHOST | NI_NUMERICSERV;
234
-
235
- switch ((sa.ss.ss_family = r->idiag_family)) {
217
+ socklen_t hostlen;
218
+ socklen_t portlen = (socklen_t)sizeof("65535");
219
+ int n;
220
+ const void *src = r->id.idiag_src;
221
+
222
+ switch (r->idiag_family) {
236
223
  case AF_INET: {
237
- sa.in.sin_port = r->id.idiag_sport;
238
- sa.in.sin_addr.s_addr = r->id.idiag_src[0];
239
- keylen = INET_ADDRSTRLEN;
240
- alloca_len = keylen + 1 + portlen;
241
- key = alloca(alloca_len);
242
- key[keylen] = 0; /* will be ':' later */
243
- port = key + keylen + 1;
244
- rc = getnameinfo(&sa.sa, len,
245
- key, keylen, port, portlen, flags);
224
+ hostlen = INET_ADDRSTRLEN;
225
+ alloca_len = hostlen + portlen;
226
+ host = key = alloca(alloca_len);
246
227
  break;
247
228
  }
248
229
  case AF_INET6: {
249
- sa.in6.sin6_port = r->id.idiag_sport;
250
- memcpy(&sa.in6.sin6_addr, &r->id.idiag_src, sizeof(__be32[4]));
251
- keylen = INET6_ADDRSTRLEN;
252
- /* [ ] */
253
- alloca_len = 1 + keylen + 1 + 1 + portlen;
230
+ hostlen = INET6_ADDRSTRLEN;
231
+ alloca_len = 1 + hostlen + 1 + portlen;
254
232
  key = alloca(alloca_len);
255
- *key = '[';
256
- key[1 + keylen + 1] = 0; /* will be ':' later */
257
- port = 1 + key + keylen + 1 + 1;
258
- rc = getnameinfo(&sa.sa, len,
259
- key + 1, keylen, port, portlen, flags);
233
+ host = key + 1;
260
234
  break;
261
235
  }
262
236
  default:
263
237
  assert(0 && "unsupported address family, could that be IPv7?!");
264
238
  }
265
- if (rc != 0) {
266
- fprintf(stderr, "BUG: getnameinfo: %s\n", gai_strerror(rc));
267
- bug_warn();
268
- *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';
269
243
  }
270
-
271
- keylen = strlen(key);
272
- portlen = strlen(port);
273
-
274
- switch (sa.ss.ss_family) {
244
+ hostlen = (socklen_t)strlen(host);
245
+ switch (r->idiag_family) {
275
246
  case AF_INET:
276
- key[keylen] = ':';
277
- memmove(key + keylen + 1, port, portlen + 1);
247
+ host[hostlen] = ':';
248
+ port = host + hostlen + 1;
278
249
  break;
279
250
  case AF_INET6:
280
- key[keylen] = ']';
281
- key[keylen + 1] = ':';
282
- memmove(key + keylen + 2, port, portlen + 1);
283
- keylen++;
251
+ key[0] = '[';
252
+ host[hostlen] = ']';
253
+ host[hostlen + 1] = ':';
254
+ port = host + hostlen + 2;
284
255
  break;
285
256
  default:
286
257
  assert(0 && "unsupported address family, could that be IPv7?!");
287
258
  }
288
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
+
289
266
  if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
290
267
  return stats;
291
268
 
292
269
  old_key = key;
293
270
 
294
271
  if (r->idiag_state == TCP_ESTABLISHED) {
295
- int n = snprintf(key, alloca_len, "%s:%u",
296
- addr_any(sa.ss.ss_family),
272
+ n = snprintf(key, alloca_len, "%s:%u",
273
+ addr_any(r->idiag_family),
297
274
  ntohs(r->id.idiag_sport));
298
275
  if (n <= 0) {
299
- fprintf(stderr, "BUG: snprintf: %d\n", n);
300
- bug_warn();
276
+ bug_warn_nogvl("BUG: snprintf: %d\n", n);
277
+ *key = '\0';
301
278
  }
302
279
  if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
303
280
  return stats;
@@ -310,8 +287,9 @@ static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
310
287
  memcpy(key, old_key, n + 1);
311
288
  }
312
289
  } else {
313
- key = xmalloc(keylen + 1 + portlen + 1);
314
- 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);
315
293
  }
316
294
  stats = xcalloc(1, sizeof(struct listen_stats));
317
295
  st_insert(table, (st_data_t)key, (st_data_t)stats);
@@ -391,8 +369,8 @@ static void prep_diag_args(
391
369
 
392
370
  nladdr->nl_family = AF_NETLINK;
393
371
 
394
- req->nlh.nlmsg_len = sizeof(struct diag_req) +
395
- RTA_LENGTH(args->iov[2].iov_len);
372
+ req->nlh.nlmsg_len = (unsigned int)(sizeof(struct diag_req) +
373
+ RTA_LENGTH(args->iov[2].iov_len));
396
374
  req->nlh.nlmsg_type = TCPDIAG_GETSOCK;
397
375
  req->nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
398
376
  req->nlh.nlmsg_pid = getpid();
@@ -465,12 +443,12 @@ static VALUE diag(void *ptr)
465
443
  }
466
444
  }
467
445
  out:
468
- {
446
+ /* prepare to raise, free memory before reacquiring GVL */
447
+ if (err && args->table) {
469
448
  int save_errno = errno;
470
- if (err && args->table) {
471
- st_foreach(args->table, st_free_data, 0);
472
- st_free_table(args->table);
473
- }
449
+
450
+ st_foreach(args->table, st_free_data, 0);
451
+ st_free_table(args->table);
474
452
  errno = save_errno;
475
453
  }
476
454
  return (VALUE)err;
@@ -584,6 +562,10 @@ static void gen_bytecode(struct iovec *iov, union any_addr *inet)
584
562
  }
585
563
  }
586
564
 
565
+ /*
566
+ * n.b. we may safely raise here because an error will cause diag()
567
+ * to free args->table
568
+ */
587
569
  static void nl_errcheck(VALUE r)
588
570
  {
589
571
  const char *err = (const char *)r;
@@ -604,11 +586,18 @@ static VALUE tcp_stats(struct nogvl_args *args, VALUE addr)
604
586
  gen_bytecode(&args->iov[2], &query_addr);
605
587
 
606
588
  memset(&args->stats, 0, sizeof(struct listen_stats));
607
- nl_errcheck(rb_thread_io_blocking_region(diag, args, args->fd));
589
+ nl_errcheck(rd_fd_region(diag, args, args->fd));
608
590
 
609
591
  return rb_listen_stats(&args->stats);
610
592
  }
611
593
 
594
+ static int drop_placeholders(st_data_t k, st_data_t v, st_data_t ign)
595
+ {
596
+ if ((VALUE)v == Qtrue)
597
+ return ST_DELETE;
598
+ return ST_CONTINUE;
599
+ }
600
+
612
601
  /*
613
602
  * call-seq:
614
603
  * Raindrops::Linux.tcp_listener_stats([addrs[, sock]]) => hash
@@ -649,10 +638,9 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
649
638
  case T_ARRAY: {
650
639
  long i;
651
640
  long len = RARRAY_LEN(addrs);
652
- VALUE cur;
653
641
 
654
642
  if (len == 1) {
655
- cur = rb_ary_entry(addrs, 0);
643
+ VALUE cur = rb_ary_entry(addrs, 0);
656
644
 
657
645
  rb_hash_aset(rv, cur, tcp_stats(&args, cur));
658
646
  return rv;
@@ -662,7 +650,7 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
662
650
  VALUE cur = rb_ary_entry(addrs, i);
663
651
 
664
652
  parse_addr(&check, cur);
665
- rb_hash_aset(rv, cur, Qtrue);
653
+ rb_hash_aset(rv, cur, Qtrue /* placeholder */);
666
654
  }
667
655
  /* fall through */
668
656
  }
@@ -675,11 +663,14 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
675
663
  "addr must be an array of strings, a string, or nil");
676
664
  }
677
665
 
678
- nl_errcheck(rb_thread_io_blocking_region(diag, &args, args.fd));
666
+ nl_errcheck(rd_fd_region(diag, &args, args.fd));
679
667
 
680
668
  st_foreach(args.table, NIL_P(addrs) ? st_to_hash : st_AND_hash, rv);
681
669
  st_free_table(args.table);
682
670
 
671
+ if (RHASH_SIZE(rv) > 1)
672
+ rb_hash_foreach(rv, drop_placeholders, Qfalse);
673
+
683
674
  /* let GC deal with corner cases */
684
675
  if (argc < 2) rb_io_close(sock);
685
676
  return rv;
@@ -687,11 +678,12 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
687
678
 
688
679
  void Init_raindrops_linux_inet_diag(void)
689
680
  {
690
- VALUE cRaindrops = rb_const_get(rb_cObject, rb_intern("Raindrops"));
681
+ VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
691
682
  VALUE mLinux = rb_define_module_under(cRaindrops, "Linux");
683
+ VALUE Socket;
692
684
 
693
685
  rb_require("socket");
694
- cIDSock = rb_const_get(rb_cObject, rb_intern("Socket"));
686
+ Socket = rb_const_get(rb_cObject, rb_intern("Socket"));
695
687
  id_new = rb_intern("new");
696
688
 
697
689
  /*
@@ -700,10 +692,11 @@ void Init_raindrops_linux_inet_diag(void)
700
692
  * This is a subclass of +Socket+ specifically for talking
701
693
  * to the inet_diag facility of Netlink.
702
694
  */
703
- cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket", cIDSock);
695
+ cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket", Socket);
704
696
  rb_define_singleton_method(cIDSock, "new", ids_s_new, 0);
705
697
 
706
698
  cListenStats = rb_const_get(cRaindrops, rb_intern("ListenStats"));
699
+ rb_gc_register_mark_object(cListenStats); /* pin */
707
700
 
708
701
  rb_define_module_function(mLinux, "tcp_listener_stats",
709
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
  }