rdoc: 100% documentation coverage!
[raindrops.git] / ext / raindrops / raindrops.c
blobdbb076ab206c9831ee7d4d7081d1687385ff9b3a
1 #include <ruby.h>
2 #include <unistd.h>
3 #include <sys/mman.h>
4 #include <assert.h>
5 #include <errno.h>
6 #include <stddef.h>
7 #include "raindrops_atomic.h"
9 #ifndef SIZET2NUM
10 # define SIZET2NUM(x) ULONG2NUM(x)
11 #endif
14 * most modern CPUs have a cache-line size of 64 or 128.
15 * We choose a bigger one by default since our structure is not
16 * heavily used
18 static size_t raindrop_size = 128;
20 /* each raindrop is a counter */
21 struct raindrop {
22 unsigned long counter;
23 } __attribute__((packed));
25 /* allow mmap-ed regions to store more than one raindrop */
26 struct raindrops {
27 long size;
28 struct raindrop *drops;
31 /* called by GC */
32 static void evaporate(void *ptr)
34 struct raindrops *r = ptr;
36 if (r->drops) {
37 int rv = munmap(r->drops, raindrop_size * r->size);
38 if (rv != 0)
39 rb_bug("munmap failed in gc: %s", strerror(errno));
42 xfree(ptr);
45 /* automatically called at creation (before initialize) */
46 static VALUE alloc(VALUE klass)
48 struct raindrops *r;
50 return Data_Make_Struct(klass, struct raindrops, NULL, evaporate, r);
53 static struct raindrops *get(VALUE self)
55 struct raindrops *r;
57 Data_Get_Struct(self, struct raindrops, r);
59 return r;
63 * call-seq:
64 * Raindrops.new(size) -> raindrops object
66 * Initializes a Raindrops object to hold +size+ counters. +size+ is
67 * only a hint and the actual number of counters the object has is
68 * dependent on the CPU model, number of cores, and page size of
69 * the machine. The actual size of the object will always be equal
70 * or greater than the specified +size+.
72 static VALUE init(VALUE self, VALUE size)
74 struct raindrops *r = get(self);
75 int tries = 1;
77 if (r->drops)
78 rb_raise(rb_eRuntimeError, "already initialized");
80 r->size = NUM2LONG(size);
81 if (r->size < 1)
82 rb_raise(rb_eArgError, "size must be >= 1");
84 retry:
85 r->drops = mmap(NULL, raindrop_size * r->size,
86 PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
87 if (r->drops == MAP_FAILED) {
88 r->drops = NULL;
89 if ((errno == EAGAIN || errno == ENOMEM) && tries-- > 0) {
90 rb_gc();
91 goto retry;
93 rb_sys_fail("mmap");
96 return self;
100 * call-seq:
101 * rd.dup -> rd_copy
103 * Duplicates and snapshots the current state of a Raindrops object.
105 static VALUE init_copy(VALUE dest, VALUE source)
107 struct raindrops *dst = get(dest);
108 struct raindrops *src = get(source);
110 init(dest, LONG2NUM(src->size));
111 memcpy(dst->drops, src->drops, raindrop_size * src->size);
113 return dest;
116 static unsigned long *addr_of(VALUE self, VALUE index)
118 struct raindrops *r = get(self);
119 unsigned long off = FIX2ULONG(index) * raindrop_size;
121 if (off >= raindrop_size * r->size)
122 rb_raise(rb_eArgError, "offset overrun");
124 return (unsigned long *)((unsigned long)r->drops + off);
127 static unsigned long incr_decr_arg(int argc, const VALUE *argv)
129 if (argc > 2 || argc < 1)
130 rb_raise(rb_eArgError,
131 "wrong number of arguments (%d for 1+)", argc);
133 return argc == 2 ? NUM2ULONG(argv[1]) : 1;
137 * call-seq:
138 * rd.incr(index[, number]) -> result
140 * Increments the value referred to by the +index+ by +number+.
141 * +number+ defaults to +1+ if unspecified.
143 static VALUE incr(int argc, VALUE *argv, VALUE self)
145 unsigned long nr = incr_decr_arg(argc, argv);
147 return ULONG2NUM(__sync_add_and_fetch(addr_of(self, argv[0]), nr));
151 * call-seq:
152 * rd.decr(index[, number]) -> result
154 * Decrements the value referred to by the +index+ by +number+.
155 * +number+ defaults to +1+ if unspecified.
157 static VALUE decr(int argc, VALUE *argv, VALUE self)
159 unsigned long nr = incr_decr_arg(argc, argv);
161 return ULONG2NUM(__sync_sub_and_fetch(addr_of(self, argv[0]), nr));
165 * call-seq:
166 * rd.to_ary -> Array
168 * converts the Raindrops structure to an Array
170 static VALUE to_ary(VALUE self)
172 struct raindrops *r = get(self);
173 VALUE rv = rb_ary_new2(r->size);
174 long i;
175 unsigned long base = (unsigned long)r->drops;
177 for (i = 0; i < r->size; i++) {
178 rb_ary_push(rv, ULONG2NUM(*((unsigned long *)base)));
179 base += raindrop_size;
182 return rv;
186 * call-seq:
187 * rd.size -> Integer
189 * Returns the number of counters a Raindrops object can hold. Due to
190 * page alignment, this is always equal or greater than the number of
191 * requested slots passed to Raindrops.new
193 static VALUE size(VALUE self)
195 return LONG2NUM(get(self)->size);
199 * call-seq:
200 * rd[index] = value
202 * Assigns +value+ to the slot designated by +index+
204 static VALUE aset(VALUE self, VALUE index, VALUE value)
206 unsigned long *addr = addr_of(self, index);
208 *addr = NUM2ULONG(value);
210 return value;
214 * call-seq:
215 * rd[index] -> value
217 * Returns the value of the slot designated by +index+
219 static VALUE aref(VALUE self, VALUE index)
221 return ULONG2NUM(*addr_of(self, index));
224 #ifdef __linux__
225 void Init_raindrops_linux_inet_diag(void);
226 void Init_raindrops_linux_tcp_info(void);
227 #endif
229 #ifndef _SC_NPROCESSORS_ONLN
230 # ifdef _SC_NPROC_ONLN
231 # define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
232 # elif defined _SC_CRAY_NCPU
233 # define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
234 # endif
235 #endif
237 void Init_raindrops_ext(void)
239 VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
240 long tmp = 2;
242 #ifdef _SC_NPROCESSORS_ONLN
243 tmp = sysconf(_SC_NPROCESSORS_ONLN);
244 #endif
245 /* no point in padding on single CPU machines */
246 if (tmp == 1)
247 raindrop_size = sizeof(unsigned long);
248 #ifdef _SC_LEVEL1_DCACHE_LINESIZE
249 if (tmp != 1) {
250 tmp = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
251 if (tmp > 0)
252 raindrop_size = (size_t)tmp;
254 #endif
257 * The size (in bytes) of a slot in a Raindrops object.
258 * This is the size of a word on single CPU systems and
259 * the size of the L1 cache line size if detectable.
261 * Defaults to 128 bytes if undetectable.
263 rb_define_const(cRaindrops, "SIZE", SIZET2NUM(raindrop_size));
266 * The maximum value a raindrop counter can hold
268 rb_define_const(cRaindrops, "MAX", ULONG2NUM((unsigned long)-1));
270 rb_define_alloc_func(cRaindrops, alloc);
272 rb_define_method(cRaindrops, "initialize", init, 1);
273 rb_define_method(cRaindrops, "incr", incr, -1);
274 rb_define_method(cRaindrops, "decr", decr, -1);
275 rb_define_method(cRaindrops, "to_ary", to_ary, 0);
276 rb_define_method(cRaindrops, "[]", aref, 1);
277 rb_define_method(cRaindrops, "[]=", aset, 2);
278 rb_define_method(cRaindrops, "size", size, 0);
279 rb_define_method(cRaindrops, "initialize_copy", init_copy, 1);
281 #ifdef __linux__
282 Init_raindrops_linux_inet_diag();
283 Init_raindrops_linux_tcp_info();
284 #endif