raindrops 0.4.1 → 0.5.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. data/.document +2 -1
  2. data/.gitignore +4 -0
  3. data/.wrongdoc.yml +4 -0
  4. data/GIT-VERSION-GEN +1 -1
  5. data/GNUmakefile +2 -196
  6. data/Gemfile +7 -0
  7. data/LICENSE +1 -1
  8. data/README +17 -47
  9. data/Rakefile +0 -104
  10. data/examples/linux-listener-stats.rb +123 -0
  11. data/examples/{config.ru → middleware.ru} +1 -1
  12. data/examples/watcher.ru +4 -0
  13. data/examples/watcher_demo.ru +13 -0
  14. data/examples/zbatery.conf.rb +13 -0
  15. data/ext/raindrops/extconf.rb +5 -0
  16. data/ext/raindrops/linux_inet_diag.c +449 -151
  17. data/ext/raindrops/linux_tcp_info.c +170 -0
  18. data/ext/raindrops/my_fileno.h +36 -0
  19. data/ext/raindrops/raindrops.c +232 -20
  20. data/lib/raindrops.rb +20 -7
  21. data/lib/raindrops/aggregate.rb +8 -0
  22. data/lib/raindrops/aggregate/last_data_recv.rb +86 -0
  23. data/lib/raindrops/aggregate/pmq.rb +239 -0
  24. data/lib/raindrops/last_data_recv.rb +100 -0
  25. data/lib/raindrops/linux.rb +26 -16
  26. data/lib/raindrops/middleware.rb +112 -41
  27. data/lib/raindrops/middleware/proxy.rb +34 -0
  28. data/lib/raindrops/struct.rb +15 -0
  29. data/lib/raindrops/watcher.rb +362 -0
  30. data/pkg.mk +171 -0
  31. data/raindrops.gemspec +10 -20
  32. data/test/ipv6_enabled.rb +10 -0
  33. data/test/rack_unicorn.rb +12 -0
  34. data/test/test_aggregate_pmq.rb +65 -0
  35. data/test/test_inet_diag_socket.rb +13 -0
  36. data/test/test_last_data_recv_unicorn.rb +69 -0
  37. data/test/test_linux.rb +55 -57
  38. data/test/test_linux_all_tcp_listen_stats.rb +66 -0
  39. data/test/test_linux_all_tcp_listen_stats_leak.rb +43 -0
  40. data/test/test_linux_ipv6.rb +158 -0
  41. data/test/test_linux_tcp_info.rb +61 -0
  42. data/test/test_middleware.rb +15 -2
  43. data/test/test_middleware_unicorn.rb +37 -0
  44. data/test/test_middleware_unicorn_ipv6.rb +37 -0
  45. data/test/test_raindrops.rb +65 -1
  46. data/test/test_raindrops_gc.rb +23 -1
  47. data/test/test_watcher.rb +85 -0
  48. metadata +69 -22
  49. data/examples/linux-tcp-listener-stats.rb +0 -44
@@ -0,0 +1,170 @@
1
+ #include <ruby.h>
2
+ #include <sys/socket.h>
3
+ #include <netinet/in.h>
4
+ #include <linux/tcp.h>
5
+ #ifdef TCP_INFO
6
+ #include "my_fileno.h"
7
+
8
+ #define TCPI_ATTR_READER(x) \
9
+ static VALUE tcp_info_##x(VALUE self) \
10
+ { \
11
+ struct tcp_info *info = DATA_PTR(self); \
12
+ return UINT2NUM((uint32_t)info->tcpi_##x); \
13
+ }
14
+
15
+ TCPI_ATTR_READER(state)
16
+ TCPI_ATTR_READER(ca_state)
17
+ TCPI_ATTR_READER(retransmits)
18
+ TCPI_ATTR_READER(probes)
19
+ TCPI_ATTR_READER(backoff)
20
+ TCPI_ATTR_READER(options)
21
+ TCPI_ATTR_READER(snd_wscale)
22
+ TCPI_ATTR_READER(rcv_wscale)
23
+ TCPI_ATTR_READER(rto)
24
+ TCPI_ATTR_READER(ato)
25
+ TCPI_ATTR_READER(snd_mss)
26
+ TCPI_ATTR_READER(rcv_mss)
27
+ TCPI_ATTR_READER(unacked)
28
+ TCPI_ATTR_READER(sacked)
29
+ TCPI_ATTR_READER(lost)
30
+ TCPI_ATTR_READER(retrans)
31
+ TCPI_ATTR_READER(fackets)
32
+ TCPI_ATTR_READER(last_data_sent)
33
+ TCPI_ATTR_READER(last_ack_sent)
34
+ TCPI_ATTR_READER(last_data_recv)
35
+ TCPI_ATTR_READER(last_ack_recv)
36
+ TCPI_ATTR_READER(pmtu)
37
+ TCPI_ATTR_READER(rcv_ssthresh)
38
+ TCPI_ATTR_READER(rtt)
39
+ TCPI_ATTR_READER(rttvar)
40
+ TCPI_ATTR_READER(snd_ssthresh)
41
+ TCPI_ATTR_READER(snd_cwnd)
42
+ TCPI_ATTR_READER(advmss)
43
+ TCPI_ATTR_READER(reordering)
44
+ TCPI_ATTR_READER(rcv_rtt)
45
+ TCPI_ATTR_READER(rcv_space)
46
+ TCPI_ATTR_READER(total_retrans)
47
+
48
+ static VALUE alloc(VALUE klass)
49
+ {
50
+ struct tcp_info *info = xmalloc(sizeof(struct tcp_info));
51
+
52
+ /* Data_Make_Struct has an extra memset 0 which is so wasteful */
53
+ return Data_Wrap_Struct(klass, NULL, -1, info);
54
+ }
55
+
56
+ /*
57
+ * call-seq:
58
+ *
59
+ * Raindrops::TCP_Info.new(tcp_socket) -> TCP_Info object
60
+ *
61
+ * Reads a TCP_Info object from any given +tcp_socket+. See the tcp(7)
62
+ * manpage and /usr/include/linux/tcp.h for more details.
63
+ */
64
+ static VALUE init(VALUE self, VALUE io)
65
+ {
66
+ int fd = my_fileno(io);
67
+ struct tcp_info *info = DATA_PTR(self);
68
+ socklen_t len = (socklen_t)sizeof(struct tcp_info);
69
+ int rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, info, &len);
70
+
71
+ if (rc != 0)
72
+ rb_sys_fail("getsockopt");
73
+
74
+ return self;
75
+ }
76
+
77
+ void Init_raindrops_linux_tcp_info(void)
78
+ {
79
+ VALUE cRaindrops = rb_const_get(rb_cObject, rb_intern("Raindrops"));
80
+ VALUE cTCP_Info;
81
+
82
+ /*
83
+ * Document-class: Raindrops::TCP_Info
84
+ *
85
+ * This is used to wrap "struct tcp_info" as described in tcp(7)
86
+ * and /usr/include/linux/tcp.h. The following readers methods
87
+ * are defined corresponding to the "tcpi_" fields in the
88
+ * tcp_info struct.
89
+ *
90
+ * In particular, the +last_data_recv+ field is useful for measuring
91
+ * the amount of time a client spent in the listen queue before
92
+ * +accept()+, but only if +TCP_DEFER_ACCEPT+ is used with the
93
+ * listen socket (it is on by default in Unicorn).
94
+ *
95
+ * - state
96
+ * - ca_state
97
+ * - retransmits
98
+ * - probes
99
+ * - backoff
100
+ * - options
101
+ * - snd_wscale
102
+ * - rcv_wscale
103
+ * - rto
104
+ * - ato
105
+ * - snd_mss
106
+ * - rcv_mss
107
+ * - unacked
108
+ * - sacked
109
+ * - lost
110
+ * - retrans
111
+ * - fackets
112
+ * - last_data_sent
113
+ * - last_ack_sent
114
+ * - last_data_recv
115
+ * - last_ack_recv
116
+ * - pmtu
117
+ * - rcv_ssthresh
118
+ * - rtt
119
+ * - rttvar
120
+ * - snd_ssthresh
121
+ * - snd_cwnd
122
+ * - advmss
123
+ * - reordering
124
+ * - rcv_rtt
125
+ * - rcv_space
126
+ * - total_retrans
127
+ *
128
+ * http://kernel.org/doc/man-pages/online/pages/man7/tcp.7.html
129
+ */
130
+ cTCP_Info = rb_define_class_under(cRaindrops, "TCP_Info", rb_cObject);
131
+ rb_define_alloc_func(cTCP_Info, alloc);
132
+ rb_define_private_method(cTCP_Info, "initialize", init, 1);
133
+
134
+ #define TCPI_DEFINE_METHOD(x) \
135
+ rb_define_method(cTCP_Info, #x, tcp_info_##x, 0)
136
+
137
+ TCPI_DEFINE_METHOD(state);
138
+ TCPI_DEFINE_METHOD(ca_state);
139
+ TCPI_DEFINE_METHOD(retransmits);
140
+ TCPI_DEFINE_METHOD(probes);
141
+ TCPI_DEFINE_METHOD(backoff);
142
+ TCPI_DEFINE_METHOD(options);
143
+ TCPI_DEFINE_METHOD(snd_wscale);
144
+ TCPI_DEFINE_METHOD(rcv_wscale);
145
+ TCPI_DEFINE_METHOD(rto);
146
+ TCPI_DEFINE_METHOD(ato);
147
+ TCPI_DEFINE_METHOD(snd_mss);
148
+ TCPI_DEFINE_METHOD(rcv_mss);
149
+ TCPI_DEFINE_METHOD(unacked);
150
+ TCPI_DEFINE_METHOD(sacked);
151
+ TCPI_DEFINE_METHOD(lost);
152
+ TCPI_DEFINE_METHOD(retrans);
153
+ TCPI_DEFINE_METHOD(fackets);
154
+ TCPI_DEFINE_METHOD(last_data_sent);
155
+ TCPI_DEFINE_METHOD(last_ack_sent);
156
+ TCPI_DEFINE_METHOD(last_data_recv);
157
+ TCPI_DEFINE_METHOD(last_ack_recv);
158
+ TCPI_DEFINE_METHOD(pmtu);
159
+ TCPI_DEFINE_METHOD(rcv_ssthresh);
160
+ TCPI_DEFINE_METHOD(rtt);
161
+ TCPI_DEFINE_METHOD(rttvar);
162
+ TCPI_DEFINE_METHOD(snd_ssthresh);
163
+ TCPI_DEFINE_METHOD(snd_cwnd);
164
+ TCPI_DEFINE_METHOD(advmss);
165
+ TCPI_DEFINE_METHOD(reordering);
166
+ TCPI_DEFINE_METHOD(rcv_rtt);
167
+ TCPI_DEFINE_METHOD(rcv_space);
168
+ TCPI_DEFINE_METHOD(total_retrans);
169
+ }
170
+ #endif /* TCP_INFO */
@@ -0,0 +1,36 @@
1
+ #include <ruby.h>
2
+ #ifdef HAVE_RUBY_IO_H
3
+ # include <ruby/io.h>
4
+ #else
5
+ # include <stdio.h>
6
+ # include <rubyio.h>
7
+ #endif
8
+
9
+ #if ! HAVE_RB_IO_T
10
+ # define rb_io_t OpenFile
11
+ #endif
12
+
13
+ #ifdef GetReadFile
14
+ # define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
15
+ #else
16
+ # if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
17
+ # define FPTR_TO_FD(fptr) fileno(fptr->f)
18
+ # else
19
+ # define FPTR_TO_FD(fptr) fptr->fd
20
+ # endif
21
+ #endif
22
+
23
+ static int my_fileno(VALUE io)
24
+ {
25
+ rb_io_t *fptr;
26
+ int fd;
27
+
28
+ if (TYPE(io) != T_FILE)
29
+ io = rb_convert_type(io, T_FILE, "IO", "to_io");
30
+ GetOpenFile(io, fptr);
31
+ fd = FPTR_TO_FD(fptr);
32
+
33
+ if (fd < 0)
34
+ rb_raise(rb_eIOError, "closed stream");
35
+ return fd;
36
+ }
@@ -9,6 +9,9 @@
9
9
  #ifndef SIZET2NUM
10
10
  # define SIZET2NUM(x) ULONG2NUM(x)
11
11
  #endif
12
+ #ifndef NUM2SIZET
13
+ # define NUM2SIZET(x) NUM2ULONG(x)
14
+ #endif
12
15
 
13
16
  /*
14
17
  * most modern CPUs have a cache-line size of 64 or 128.
@@ -16,25 +19,31 @@
16
19
  * heavily used
17
20
  */
18
21
  static size_t raindrop_size = 128;
22
+ static size_t rd_page_size;
23
+
24
+ #define PAGE_MASK (~(rd_page_size - 1))
25
+ #define PAGE_ALIGN(addr) (((addr) + rd_page_size - 1) & PAGE_MASK)
19
26
 
20
27
  /* each raindrop is a counter */
21
28
  struct raindrop {
22
29
  unsigned long counter;
23
30
  } __attribute__((packed));
24
31
 
25
- /* allow mmap-ed regions can store more than one raindrop */
32
+ /* allow mmap-ed regions to store more than one raindrop */
26
33
  struct raindrops {
27
- long size;
34
+ size_t size;
35
+ size_t capa;
36
+ pid_t pid;
28
37
  struct raindrop *drops;
29
38
  };
30
39
 
31
40
  /* called by GC */
32
- static void evaporate(void *ptr)
41
+ static void gcfree(void *ptr)
33
42
  {
34
43
  struct raindrops *r = ptr;
35
44
 
36
- if (r->drops) {
37
- int rv = munmap(r->drops, raindrop_size * r->size);
45
+ if (r->drops != MAP_FAILED) {
46
+ int rv = munmap(r->drops, raindrop_size * r->capa);
38
47
  if (rv != 0)
39
48
  rb_bug("munmap failed in gc: %s", strerror(errno));
40
49
  }
@@ -46,8 +55,10 @@ static void evaporate(void *ptr)
46
55
  static VALUE alloc(VALUE klass)
47
56
  {
48
57
  struct raindrops *r;
58
+ VALUE rv = Data_Make_Struct(klass, struct raindrops, NULL, gcfree, r);
49
59
 
50
- return Data_Make_Struct(klass, struct raindrops, NULL, evaporate, r);
60
+ r->drops = MAP_FAILED;
61
+ return rv;
51
62
  }
52
63
 
53
64
  static struct raindrops *get(VALUE self)
@@ -56,44 +67,147 @@ static struct raindrops *get(VALUE self)
56
67
 
57
68
  Data_Get_Struct(self, struct raindrops, r);
58
69
 
70
+ if (r->drops == MAP_FAILED)
71
+ rb_raise(rb_eStandardError, "invalid or freed Raindrops");
72
+
59
73
  return r;
60
74
  }
61
75
 
62
- /* initializes a Raindrops object to hold +size+ elements */
76
+ /*
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+.
85
+ */
63
86
  static VALUE init(VALUE self, VALUE size)
64
87
  {
65
- struct raindrops *r = get(self);
88
+ struct raindrops *r = DATA_PTR(self);
66
89
  int tries = 1;
90
+ size_t tmp;
67
91
 
68
- if (r->drops)
92
+ if (r->drops != MAP_FAILED)
69
93
  rb_raise(rb_eRuntimeError, "already initialized");
70
94
 
71
- r->size = NUM2LONG(size);
95
+ r->size = NUM2SIZET(size);
72
96
  if (r->size < 1)
73
97
  rb_raise(rb_eArgError, "size must be >= 1");
74
98
 
99
+ tmp = PAGE_ALIGN(raindrop_size * r->size);
100
+ r->capa = tmp / raindrop_size;
101
+ assert(PAGE_ALIGN(raindrop_size * r->capa) == tmp && "not aligned");
102
+
75
103
  retry:
76
- r->drops = mmap(NULL, raindrop_size * r->size,
104
+ r->drops = mmap(NULL, tmp,
77
105
  PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
78
106
  if (r->drops == MAP_FAILED) {
79
- r->drops = NULL;
80
107
  if ((errno == EAGAIN || errno == ENOMEM) && tries-- > 0) {
81
108
  rb_gc();
82
109
  goto retry;
83
110
  }
84
111
  rb_sys_fail("mmap");
85
112
  }
113
+ r->pid = getpid();
86
114
 
87
115
  return self;
88
116
  }
89
117
 
90
- /* :nodoc */
118
+ /*
119
+ * mremap() is currently broken with MAP_SHARED
120
+ * https://bugzilla.kernel.org/show_bug.cgi?id=8691
121
+ */
122
+ #if defined(HAVE_MREMAP) && !defined(MREMAP_WORKS_WITH_MAP_SHARED)
123
+ # undef HAVE_MREMAP
124
+ #endif
125
+
126
+ #ifdef HAVE_MREMAP
127
+ #ifndef MREMAP_MAYMOVE
128
+ # warn MREMAP_MAYMOVE undefined
129
+ # define MREMAP_MAYMOVE 0
130
+ #endif
131
+ static void resize(struct raindrops *r, size_t new_rd_size)
132
+ {
133
+ size_t old_size = raindrop_size * r->capa;
134
+ size_t new_size = PAGE_ALIGN(raindrop_size * new_rd_size);
135
+ void *old_address = r->drops;
136
+ void *rv;
137
+
138
+ if (r->pid != getpid())
139
+ rb_raise(rb_eRuntimeError, "cannot mremap() from child");
140
+
141
+ rv = mremap(old_address, old_size, new_size, MREMAP_MAYMOVE);
142
+ if (rv == MAP_FAILED) {
143
+ if (errno == EAGAIN || errno == ENOMEM) {
144
+ rb_gc();
145
+ rv = mremap(old_address, old_size, new_size, 0);
146
+ }
147
+ if (rv == MAP_FAILED)
148
+ rb_sys_fail("mremap");
149
+ }
150
+ r->drops = rv;
151
+ r->size = new_rd_size;
152
+ r->capa = new_size / raindrop_size;
153
+ assert(r->capa >= r->size && "bad sizing");
154
+ }
155
+ #else /* ! HAVE_MREMAP */
156
+ /*
157
+ * we cannot use munmap + mmap to reallocate the buffer since it may
158
+ * already be shared by other processes, so we just fail
159
+ */
160
+ static void resize(struct raindrops *r, size_t new_rd_size)
161
+ {
162
+ rb_raise(rb_eRangeError, "mremap(2) is not available");
163
+ }
164
+ #endif /* ! HAVE_MREMAP */
165
+
166
+ /*
167
+ * call-seq:
168
+ * rd.size = new_size
169
+ *
170
+ * Increases or decreases the current capacity of our Raindrop.
171
+ * Raises RangeError if +new_size+ is too big or small for the
172
+ * current backing store
173
+ */
174
+ static VALUE setsize(VALUE self, VALUE new_size)
175
+ {
176
+ size_t new_rd_size = NUM2SIZET(new_size);
177
+ struct raindrops *r = get(self);
178
+
179
+ if (new_rd_size <= r->capa)
180
+ r->size = new_rd_size;
181
+ else
182
+ resize(r, new_rd_size);
183
+
184
+ return new_size;
185
+ }
186
+
187
+ /*
188
+ * call-seq:
189
+ * rd.capa -> Integer
190
+ *
191
+ * Returns the number of slots allocated (but not necessarily used) by
192
+ * the Raindrops object.
193
+ */
194
+ static VALUE capa(VALUE self)
195
+ {
196
+ return SIZET2NUM(get(self)->capa);
197
+ }
198
+
199
+ /*
200
+ * call-seq:
201
+ * rd.dup -> rd_copy
202
+ *
203
+ * Duplicates and snapshots the current state of a Raindrops object.
204
+ */
91
205
  static VALUE init_copy(VALUE dest, VALUE source)
92
206
  {
93
- struct raindrops *dst = get(dest);
207
+ struct raindrops *dst = DATA_PTR(dest);
94
208
  struct raindrops *src = get(source);
95
209
 
96
- init(dest, LONG2NUM(src->size));
210
+ init(dest, SIZET2NUM(src->size));
97
211
  memcpy(dst->drops, src->drops, raindrop_size * src->size);
98
212
 
99
213
  return dest;
@@ -119,7 +233,13 @@ static unsigned long incr_decr_arg(int argc, const VALUE *argv)
119
233
  return argc == 2 ? NUM2ULONG(argv[1]) : 1;
120
234
  }
121
235
 
122
- /* increments the value referred to by the +index+ constant by 1 */
236
+ /*
237
+ * call-seq:
238
+ * rd.incr(index[, number]) -> result
239
+ *
240
+ * Increments the value referred to by the +index+ by +number+.
241
+ * +number+ defaults to +1+ if unspecified.
242
+ */
123
243
  static VALUE incr(int argc, VALUE *argv, VALUE self)
124
244
  {
125
245
  unsigned long nr = incr_decr_arg(argc, argv);
@@ -127,7 +247,13 @@ static VALUE incr(int argc, VALUE *argv, VALUE self)
127
247
  return ULONG2NUM(__sync_add_and_fetch(addr_of(self, argv[0]), nr));
128
248
  }
129
249
 
130
- /* decrements the value referred to by the +index+ constant by 1 */
250
+ /*
251
+ * call-seq:
252
+ * rd.decr(index[, number]) -> result
253
+ *
254
+ * Decrements the value referred to by the +index+ by +number+.
255
+ * +number+ defaults to +1+ if unspecified.
256
+ */
131
257
  static VALUE decr(int argc, VALUE *argv, VALUE self)
132
258
  {
133
259
  unsigned long nr = incr_decr_arg(argc, argv);
@@ -135,12 +261,17 @@ static VALUE decr(int argc, VALUE *argv, VALUE self)
135
261
  return ULONG2NUM(__sync_sub_and_fetch(addr_of(self, argv[0]), nr));
136
262
  }
137
263
 
138
- /* converts the raindrops structure to an Array */
264
+ /*
265
+ * call-seq:
266
+ * rd.to_ary -> Array
267
+ *
268
+ * converts the Raindrops structure to an Array
269
+ */
139
270
  static VALUE to_ary(VALUE self)
140
271
  {
141
272
  struct raindrops *r = get(self);
142
273
  VALUE rv = rb_ary_new2(r->size);
143
- long i;
274
+ size_t i;
144
275
  unsigned long base = (unsigned long)r->drops;
145
276
 
146
277
  for (i = 0; i < r->size; i++) {
@@ -151,11 +282,25 @@ static VALUE to_ary(VALUE self)
151
282
  return rv;
152
283
  }
153
284
 
285
+ /*
286
+ * call-seq:
287
+ * rd.size -> Integer
288
+ *
289
+ * Returns the number of counters a Raindrops object can hold. Due to
290
+ * page alignment, this is always equal or greater than the number of
291
+ * requested slots passed to Raindrops.new
292
+ */
154
293
  static VALUE size(VALUE self)
155
294
  {
156
- return LONG2NUM(get(self)->size);
295
+ return SIZET2NUM(get(self)->size);
157
296
  }
158
297
 
298
+ /*
299
+ * call-seq:
300
+ * rd[index] = value
301
+ *
302
+ * Assigns +value+ to the slot designated by +index+
303
+ */
159
304
  static VALUE aset(VALUE self, VALUE index, VALUE value)
160
305
  {
161
306
  unsigned long *addr = addr_of(self, index);
@@ -165,6 +310,12 @@ static VALUE aset(VALUE self, VALUE index, VALUE value)
165
310
  return value;
166
311
  }
167
312
 
313
+ /*
314
+ * call-seq:
315
+ * rd[index] -> value
316
+ *
317
+ * Returns the value of the slot designated by +index+
318
+ */
168
319
  static VALUE aref(VALUE self, VALUE index)
169
320
  {
170
321
  return ULONG2NUM(*addr_of(self, index));
@@ -172,6 +323,7 @@ static VALUE aref(VALUE self, VALUE index)
172
323
 
173
324
  #ifdef __linux__
174
325
  void Init_raindrops_linux_inet_diag(void);
326
+ void Init_raindrops_linux_tcp_info(void);
175
327
  #endif
176
328
 
177
329
  #ifndef _SC_NPROCESSORS_ONLN
@@ -182,6 +334,26 @@ void Init_raindrops_linux_inet_diag(void);
182
334
  # endif
183
335
  #endif
184
336
 
337
+ /*
338
+ * call-seq:
339
+ * rd.evaporate! -> nil
340
+ *
341
+ * Releases mmap()-ed memory allocated for the Raindrops object back
342
+ * to the OS. The Ruby garbage collector will also release memory
343
+ * automatically when it is not needed, but this forces release
344
+ * under high memory pressure.
345
+ */
346
+ static VALUE evaporate_bang(VALUE self)
347
+ {
348
+ struct raindrops *r = get(self);
349
+ void *addr = r->drops;
350
+
351
+ r->drops = MAP_FAILED;
352
+ if (munmap(addr, raindrop_size * r->capa) != 0)
353
+ rb_sys_fail("munmap");
354
+ return Qnil;
355
+ }
356
+
185
357
  void Init_raindrops_ext(void)
186
358
  {
187
359
  VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
@@ -200,8 +372,44 @@ void Init_raindrops_ext(void)
200
372
  raindrop_size = (size_t)tmp;
201
373
  }
202
374
  #endif
375
+ #if defined(_SC_PAGE_SIZE)
376
+ rd_page_size = (size_t)sysconf(_SC_PAGE_SIZE);
377
+ #elif defined(_SC_PAGESIZE)
378
+ rd_page_size = (size_t)sysconf(_SC_PAGESIZE);
379
+ #elif defined(HAVE_GETPAGESIZE)
380
+ rd_page_size = (size_t)getpagesize();
381
+ #elif defined(PAGE_SIZE)
382
+ rd_page_size = (size_t)PAGE_SIZE;
383
+ #elif defined(PAGESIZE)
384
+ rd_page_size = (size_t)PAGESIZE;
385
+ #else
386
+ # error unable to detect page size for mmap()
387
+ #endif
388
+ if ((rd_page_size == (size_t)-1) || (rd_page_size < raindrop_size))
389
+ rb_raise(rb_eRuntimeError,
390
+ "system page size invalid: %llu",
391
+ (unsigned long long)rd_page_size);
392
+
393
+ /*
394
+ * The size of one page of memory for a mmap()-ed Raindrops region.
395
+ * Typically 4096 bytes under Linux.
396
+ */
397
+ rb_define_const(cRaindrops, "PAGE_SIZE", SIZET2NUM(rd_page_size));
398
+
399
+ /*
400
+ * The size (in bytes) of a slot in a Raindrops object.
401
+ * This is the size of a word on single CPU systems and
402
+ * the size of the L1 cache line size if detectable.
403
+ *
404
+ * Defaults to 128 bytes if undetectable.
405
+ */
203
406
  rb_define_const(cRaindrops, "SIZE", SIZET2NUM(raindrop_size));
204
407
 
408
+ /*
409
+ * The maximum value a raindrop counter can hold
410
+ */
411
+ rb_define_const(cRaindrops, "MAX", ULONG2NUM((unsigned long)-1));
412
+
205
413
  rb_define_alloc_func(cRaindrops, alloc);
206
414
 
207
415
  rb_define_method(cRaindrops, "initialize", init, 1);
@@ -211,9 +419,13 @@ void Init_raindrops_ext(void)
211
419
  rb_define_method(cRaindrops, "[]", aref, 1);
212
420
  rb_define_method(cRaindrops, "[]=", aset, 2);
213
421
  rb_define_method(cRaindrops, "size", size, 0);
422
+ rb_define_method(cRaindrops, "size=", setsize, 1);
423
+ rb_define_method(cRaindrops, "capa", capa, 0);
214
424
  rb_define_method(cRaindrops, "initialize_copy", init_copy, 1);
425
+ rb_define_method(cRaindrops, "evaporate!", evaporate_bang, 0);
215
426
 
216
427
  #ifdef __linux__
217
428
  Init_raindrops_linux_inet_diag();
429
+ Init_raindrops_linux_tcp_info();
218
430
  #endif
219
431
  }