raindrops 0.13.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }