raindrops 0.4.1 → 0.5.0

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