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.
- data/.document +2 -1
- data/.gitignore +4 -0
- data/.wrongdoc.yml +4 -0
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -196
- data/Gemfile +7 -0
- data/LICENSE +1 -1
- data/README +17 -47
- data/Rakefile +0 -104
- data/examples/linux-listener-stats.rb +123 -0
- data/examples/{config.ru → middleware.ru} +1 -1
- data/examples/watcher.ru +4 -0
- data/examples/watcher_demo.ru +13 -0
- data/examples/zbatery.conf.rb +13 -0
- data/ext/raindrops/extconf.rb +5 -0
- data/ext/raindrops/linux_inet_diag.c +449 -151
- data/ext/raindrops/linux_tcp_info.c +170 -0
- data/ext/raindrops/my_fileno.h +36 -0
- data/ext/raindrops/raindrops.c +232 -20
- data/lib/raindrops.rb +20 -7
- data/lib/raindrops/aggregate.rb +8 -0
- data/lib/raindrops/aggregate/last_data_recv.rb +86 -0
- data/lib/raindrops/aggregate/pmq.rb +239 -0
- data/lib/raindrops/last_data_recv.rb +100 -0
- data/lib/raindrops/linux.rb +26 -16
- data/lib/raindrops/middleware.rb +112 -41
- data/lib/raindrops/middleware/proxy.rb +34 -0
- data/lib/raindrops/struct.rb +15 -0
- data/lib/raindrops/watcher.rb +362 -0
- data/pkg.mk +171 -0
- data/raindrops.gemspec +10 -20
- data/test/ipv6_enabled.rb +10 -0
- data/test/rack_unicorn.rb +12 -0
- data/test/test_aggregate_pmq.rb +65 -0
- data/test/test_inet_diag_socket.rb +13 -0
- data/test/test_last_data_recv_unicorn.rb +69 -0
- data/test/test_linux.rb +55 -57
- data/test/test_linux_all_tcp_listen_stats.rb +66 -0
- data/test/test_linux_all_tcp_listen_stats_leak.rb +43 -0
- data/test/test_linux_ipv6.rb +158 -0
- data/test/test_linux_tcp_info.rb +61 -0
- data/test/test_middleware.rb +15 -2
- data/test/test_middleware_unicorn.rb +37 -0
- data/test/test_middleware_unicorn_ipv6.rb +37 -0
- data/test/test_raindrops.rb +65 -1
- data/test/test_raindrops_gc.rb +23 -1
- data/test/test_watcher.rb +85 -0
- metadata +69 -22
- 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
|
+
}
|
data/ext/raindrops/raindrops.c
CHANGED
@@ -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
|
32
|
+
/* allow mmap-ed regions to store more than one raindrop */
|
26
33
|
struct raindrops {
|
27
|
-
|
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
|
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->
|
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
|
-
|
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
|
-
/*
|
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 =
|
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 =
|
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,
|
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
|
-
/*
|
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 =
|
207
|
+
struct raindrops *dst = DATA_PTR(dest);
|
94
208
|
struct raindrops *src = get(source);
|
95
209
|
|
96
|
-
init(dest,
|
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
|
-
/*
|
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
|
-
/*
|
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
|
-
/*
|
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
|
-
|
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
|
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
|
}
|