fix stupid typo in Raindrops#size=
[raindrops.git] / ext / raindrops / raindrops.c
blob7bd6f3147bbc3ce6a66cd8dcf05b1c9a388c5d6c
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
12 #ifndef NUM2SIZET
13 # define NUM2SIZET(x) NUM2ULONG(x)
14 #endif
17 * most modern CPUs have a cache-line size of 64 or 128.
18 * We choose a bigger one by default since our structure is not
19 * heavily used
21 static size_t raindrop_size = 128;
22 static size_t rd_page_size;
24 #define PAGE_MASK (~(rd_page_size - 1))
25 #define PAGE_ALIGN(addr) (((addr) + rd_page_size - 1) & PAGE_MASK)
27 /* each raindrop is a counter */
28 struct raindrop {
29 unsigned long counter;
30 } __attribute__((packed));
32 /* allow mmap-ed regions to store more than one raindrop */
33 struct raindrops {
34 long size;
35 size_t capa;
36 pid_t pid;
37 struct raindrop *drops;
40 /* called by GC */
41 static void gcfree(void *ptr)
43 struct raindrops *r = ptr;
45 if (r->drops != MAP_FAILED) {
46 int rv = munmap(r->drops, raindrop_size * r->capa);
47 if (rv != 0)
48 rb_bug("munmap failed in gc: %s", strerror(errno));
51 xfree(ptr);
54 /* automatically called at creation (before initialize) */
55 static VALUE alloc(VALUE klass)
57 struct raindrops *r;
58 VALUE rv = Data_Make_Struct(klass, struct raindrops, NULL, gcfree, r);
60 r->drops = MAP_FAILED;
61 return rv;
64 static struct raindrops *get(VALUE self)
66 struct raindrops *r;
68 Data_Get_Struct(self, struct raindrops, r);
70 if (r->drops == MAP_FAILED)
71 rb_raise(rb_eStandardError, "invalid or freed Raindrops");
73 return r;
77 * call-seq:
78 * Raindrops.new(size) -> raindrops object
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+.
86 static VALUE init(VALUE self, VALUE size)
88 struct raindrops *r = DATA_PTR(self);
89 int tries = 1;
90 size_t tmp;
92 if (r->drops != MAP_FAILED)
93 rb_raise(rb_eRuntimeError, "already initialized");
95 r->size = NUM2LONG(size);
96 if (r->size < 1)
97 rb_raise(rb_eArgError, "size must be >= 1");
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");
103 retry:
104 r->drops = mmap(NULL, tmp,
105 PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
106 if (r->drops == MAP_FAILED) {
107 if ((errno == EAGAIN || errno == ENOMEM) && tries-- > 0) {
108 rb_gc();
109 goto retry;
111 rb_sys_fail("mmap");
113 r->pid = getpid();
115 return self;
119 * mremap() is currently broken with MAP_SHARED
120 * https://bugzilla.kernel.org/show_bug.cgi?id=8691
122 #if defined(HAVE_MREMAP) && !defined(MREMAP_WORKS_WITH_MAP_SHARED)
123 # undef HAVE_MREMAP
124 #endif
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)
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;
138 if (r->pid != getpid())
139 rb_raise(rb_eRuntimeError, "cannot mremap() from child");
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);
147 if (rv == MAP_FAILED)
148 rb_sys_fail("mremap");
150 r->drops = rv;
151 r->size = new_rd_size;
152 r->capa = new_size / raindrop_size;
153 assert(r->capa >= (size_t)r->size && "bad sizing");
155 #else /* ! HAVE_MREMAP */
157 * we cannot use munmap + mmap to reallocate the buffer since it may
158 * already be shared by other processes, so we just fail
160 static void resize(struct raindrops *r, size_t new_rd_size)
162 rb_raise(rb_eRangeError, "mremap(2) is not available");
164 #endif /* ! HAVE_MREMAP */
167 * call-seq:
168 * rd.size = new_size
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
174 static VALUE setsize(VALUE self, VALUE new_size)
176 size_t new_rd_size = NUM2SIZET(new_size);
177 struct raindrops *r = get(self);
179 if (new_rd_size <= r->capa)
180 r->size = new_rd_size;
181 else
182 resize(r, new_rd_size);
184 return new_size;
188 * call-seq:
189 * rd.capa -> Integer
191 * Returns the number of slots allocated (but not necessarily used) by
192 * the Raindrops object.
194 static VALUE capa(VALUE self)
196 return SIZET2NUM(get(self)->capa);
200 * call-seq:
201 * rd.dup -> rd_copy
203 * Duplicates and snapshots the current state of a Raindrops object.
205 static VALUE init_copy(VALUE dest, VALUE source)
207 struct raindrops *dst = DATA_PTR(dest);
208 struct raindrops *src = get(source);
210 init(dest, LONG2NUM(src->size));
211 memcpy(dst->drops, src->drops, raindrop_size * src->size);
213 return dest;
216 static unsigned long *addr_of(VALUE self, VALUE index)
218 struct raindrops *r = get(self);
219 unsigned long off = FIX2ULONG(index) * raindrop_size;
221 if (off >= raindrop_size * r->size)
222 rb_raise(rb_eArgError, "offset overrun");
224 return (unsigned long *)((unsigned long)r->drops + off);
227 static unsigned long incr_decr_arg(int argc, const VALUE *argv)
229 if (argc > 2 || argc < 1)
230 rb_raise(rb_eArgError,
231 "wrong number of arguments (%d for 1+)", argc);
233 return argc == 2 ? NUM2ULONG(argv[1]) : 1;
237 * call-seq:
238 * rd.incr(index[, number]) -> result
240 * Increments the value referred to by the +index+ by +number+.
241 * +number+ defaults to +1+ if unspecified.
243 static VALUE incr(int argc, VALUE *argv, VALUE self)
245 unsigned long nr = incr_decr_arg(argc, argv);
247 return ULONG2NUM(__sync_add_and_fetch(addr_of(self, argv[0]), nr));
251 * call-seq:
252 * rd.decr(index[, number]) -> result
254 * Decrements the value referred to by the +index+ by +number+.
255 * +number+ defaults to +1+ if unspecified.
257 static VALUE decr(int argc, VALUE *argv, VALUE self)
259 unsigned long nr = incr_decr_arg(argc, argv);
261 return ULONG2NUM(__sync_sub_and_fetch(addr_of(self, argv[0]), nr));
265 * call-seq:
266 * rd.to_ary -> Array
268 * converts the Raindrops structure to an Array
270 static VALUE to_ary(VALUE self)
272 struct raindrops *r = get(self);
273 VALUE rv = rb_ary_new2(r->size);
274 long i;
275 unsigned long base = (unsigned long)r->drops;
277 for (i = 0; i < r->size; i++) {
278 rb_ary_push(rv, ULONG2NUM(*((unsigned long *)base)));
279 base += raindrop_size;
282 return rv;
286 * call-seq:
287 * rd.size -> Integer
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
293 static VALUE size(VALUE self)
295 return LONG2NUM(get(self)->size);
299 * call-seq:
300 * rd[index] = value
302 * Assigns +value+ to the slot designated by +index+
304 static VALUE aset(VALUE self, VALUE index, VALUE value)
306 unsigned long *addr = addr_of(self, index);
308 *addr = NUM2ULONG(value);
310 return value;
314 * call-seq:
315 * rd[index] -> value
317 * Returns the value of the slot designated by +index+
319 static VALUE aref(VALUE self, VALUE index)
321 return ULONG2NUM(*addr_of(self, index));
324 #ifdef __linux__
325 void Init_raindrops_linux_inet_diag(void);
326 void Init_raindrops_linux_tcp_info(void);
327 #endif
329 #ifndef _SC_NPROCESSORS_ONLN
330 # ifdef _SC_NPROC_ONLN
331 # define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
332 # elif defined _SC_CRAY_NCPU
333 # define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
334 # endif
335 #endif
338 * call-seq:
339 * rd.evaporate! -> nil
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.
346 static VALUE evaporate_bang(VALUE self)
348 struct raindrops *r = get(self);
349 void *addr = r->drops;
351 r->drops = MAP_FAILED;
352 if (munmap(addr, raindrop_size * r->capa) != 0)
353 rb_sys_fail("munmap");
354 return Qnil;
357 void Init_raindrops_ext(void)
359 VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
360 long tmp = 2;
362 #ifdef _SC_NPROCESSORS_ONLN
363 tmp = sysconf(_SC_NPROCESSORS_ONLN);
364 #endif
365 /* no point in padding on single CPU machines */
366 if (tmp == 1)
367 raindrop_size = sizeof(unsigned long);
368 #ifdef _SC_LEVEL1_DCACHE_LINESIZE
369 if (tmp != 1) {
370 tmp = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
371 if (tmp > 0)
372 raindrop_size = (size_t)tmp;
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);
394 * The size of one page of memory for a mmap()-ed Raindrops region.
395 * Typically 4096 bytes under Linux.
397 rb_define_const(cRaindrops, "PAGE_SIZE", SIZET2NUM(rd_page_size));
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.
404 * Defaults to 128 bytes if undetectable.
406 rb_define_const(cRaindrops, "SIZE", SIZET2NUM(raindrop_size));
409 * The maximum value a raindrop counter can hold
411 rb_define_const(cRaindrops, "MAX", ULONG2NUM((unsigned long)-1));
413 rb_define_alloc_func(cRaindrops, alloc);
415 rb_define_method(cRaindrops, "initialize", init, 1);
416 rb_define_method(cRaindrops, "incr", incr, -1);
417 rb_define_method(cRaindrops, "decr", decr, -1);
418 rb_define_method(cRaindrops, "to_ary", to_ary, 0);
419 rb_define_method(cRaindrops, "[]", aref, 1);
420 rb_define_method(cRaindrops, "[]=", aset, 2);
421 rb_define_method(cRaindrops, "size", size, 0);
422 rb_define_method(cRaindrops, "size=", setsize, 1);
423 rb_define_method(cRaindrops, "capa", capa, 0);
424 rb_define_method(cRaindrops, "initialize_copy", init_copy, 1);
425 rb_define_method(cRaindrops, "evaporate!", evaporate_bang, 0);
427 #ifdef __linux__
428 Init_raindrops_linux_inet_diag();
429 Init_raindrops_linux_tcp_info();
430 #endif