avoid reading errno repeatedly
[raindrops.git] / ext / raindrops / raindrops.c
blob90908390188a3c4412222585d09ea0b76f823252
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 size_t size;
35 size_t capa;
36 pid_t pid;
37 struct raindrop *drops;
40 /* called by GC */
41 static void rd_free(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 static size_t rd_memsize(const void *ptr)
56 const struct raindrops *r = ptr;
58 return r->drops == MAP_FAILED ? 0 : raindrop_size * r->capa;
61 static const rb_data_type_t rd_type = {
62 "raindrops",
63 { NULL, rd_free, rd_memsize, /* reserved */ },
64 /* parent, data, [ flags ] */
67 /* automatically called at creation (before initialize) */
68 static VALUE alloc(VALUE klass)
70 struct raindrops *r;
71 VALUE rv = TypedData_Make_Struct(klass, struct raindrops, &rd_type, r);
73 r->drops = MAP_FAILED;
74 return rv;
77 static struct raindrops *get(VALUE self)
79 struct raindrops *r;
81 TypedData_Get_Struct(self, struct raindrops, &rd_type, r);
83 if (r->drops == MAP_FAILED)
84 rb_raise(rb_eStandardError, "invalid or freed Raindrops");
86 return r;
90 * call-seq:
91 * Raindrops.new(size) -> raindrops object
93 * Initializes a Raindrops object to hold +size+ counters. +size+ is
94 * only a hint and the actual number of counters the object has is
95 * dependent on the CPU model, number of cores, and page size of
96 * the machine. The actual size of the object will always be equal
97 * or greater than the specified +size+.
99 static VALUE init(VALUE self, VALUE size)
101 struct raindrops *r = DATA_PTR(self);
102 int tries = 1;
103 size_t tmp;
105 if (r->drops != MAP_FAILED)
106 rb_raise(rb_eRuntimeError, "already initialized");
108 r->size = NUM2SIZET(size);
109 if (r->size < 1)
110 rb_raise(rb_eArgError, "size must be >= 1");
112 tmp = PAGE_ALIGN(raindrop_size * r->size);
113 r->capa = tmp / raindrop_size;
114 assert(PAGE_ALIGN(raindrop_size * r->capa) == tmp && "not aligned");
116 retry:
117 r->drops = mmap(NULL, tmp,
118 PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
119 if (r->drops == MAP_FAILED) {
120 int err = errno;
122 if ((err == EAGAIN || err == ENOMEM) && tries-- > 0) {
123 rb_gc();
124 goto retry;
126 rb_sys_fail("mmap");
128 r->pid = getpid();
130 return self;
134 * mremap() is currently broken with MAP_SHARED
135 * https://bugzilla.kernel.org/show_bug.cgi?id=8691
137 #if defined(HAVE_MREMAP) && !defined(MREMAP_WORKS_WITH_MAP_SHARED)
138 # undef HAVE_MREMAP
139 #endif
141 #ifdef HAVE_MREMAP
142 #ifndef MREMAP_MAYMOVE
143 # warn MREMAP_MAYMOVE undefined
144 # define MREMAP_MAYMOVE 0
145 #endif
146 static void resize(struct raindrops *r, size_t new_rd_size)
148 size_t old_size = raindrop_size * r->capa;
149 size_t new_size = PAGE_ALIGN(raindrop_size * new_rd_size);
150 void *old_address = r->drops;
151 void *rv;
153 if (r->pid != getpid())
154 rb_raise(rb_eRuntimeError, "cannot mremap() from child");
156 rv = mremap(old_address, old_size, new_size, MREMAP_MAYMOVE);
157 if (rv == MAP_FAILED) {
158 int err = errno;
160 if (err == EAGAIN || err == ENOMEM) {
161 rb_gc();
162 rv = mremap(old_address, old_size, new_size, 0);
164 if (rv == MAP_FAILED)
165 rb_sys_fail("mremap");
167 r->drops = rv;
168 r->size = new_rd_size;
169 r->capa = new_size / raindrop_size;
170 assert(r->capa >= r->size && "bad sizing");
172 #else /* ! HAVE_MREMAP */
174 * we cannot use munmap + mmap to reallocate the buffer since it may
175 * already be shared by other processes, so we just fail
177 static void resize(struct raindrops *r, size_t new_rd_size)
179 rb_raise(rb_eRangeError, "mremap(2) is not available");
181 #endif /* ! HAVE_MREMAP */
184 * call-seq:
185 * rd.size = new_size
187 * Increases or decreases the current capacity of our Raindrop.
188 * Raises RangeError if +new_size+ is too big or small for the
189 * current backing store
191 static VALUE setsize(VALUE self, VALUE new_size)
193 size_t new_rd_size = NUM2SIZET(new_size);
194 struct raindrops *r = get(self);
196 if (new_rd_size <= r->capa)
197 r->size = new_rd_size;
198 else
199 resize(r, new_rd_size);
201 return new_size;
205 * call-seq:
206 * rd.capa -> Integer
208 * Returns the number of slots allocated (but not necessarily used) by
209 * the Raindrops object.
211 static VALUE capa(VALUE self)
213 return SIZET2NUM(get(self)->capa);
217 * call-seq:
218 * rd.dup -> rd_copy
220 * Duplicates and snapshots the current state of a Raindrops object.
222 static VALUE init_copy(VALUE dest, VALUE source)
224 struct raindrops *dst = DATA_PTR(dest);
225 struct raindrops *src = get(source);
227 init(dest, SIZET2NUM(src->size));
228 memcpy(dst->drops, src->drops, raindrop_size * src->size);
230 return dest;
233 static unsigned long *addr_of(VALUE self, VALUE index)
235 struct raindrops *r = get(self);
236 unsigned long off = FIX2ULONG(index) * raindrop_size;
238 if (off >= raindrop_size * r->size)
239 rb_raise(rb_eArgError, "offset overrun");
241 return (unsigned long *)((unsigned long)r->drops + off);
244 static unsigned long incr_decr_arg(int argc, const VALUE *argv)
246 if (argc > 2 || argc < 1)
247 rb_raise(rb_eArgError,
248 "wrong number of arguments (%d for 1+)", argc);
250 return argc == 2 ? NUM2ULONG(argv[1]) : 1;
254 * call-seq:
255 * rd.incr(index[, number]) -> result
257 * Increments the value referred to by the +index+ by +number+.
258 * +number+ defaults to +1+ if unspecified.
260 static VALUE incr(int argc, VALUE *argv, VALUE self)
262 unsigned long nr = incr_decr_arg(argc, argv);
264 return ULONG2NUM(__sync_add_and_fetch(addr_of(self, argv[0]), nr));
268 * call-seq:
269 * rd.decr(index[, number]) -> result
271 * Decrements the value referred to by the +index+ by +number+.
272 * +number+ defaults to +1+ if unspecified.
274 static VALUE decr(int argc, VALUE *argv, VALUE self)
276 unsigned long nr = incr_decr_arg(argc, argv);
278 return ULONG2NUM(__sync_sub_and_fetch(addr_of(self, argv[0]), nr));
282 * call-seq:
283 * rd.to_ary -> Array
285 * converts the Raindrops structure to an Array
287 static VALUE to_ary(VALUE self)
289 struct raindrops *r = get(self);
290 VALUE rv = rb_ary_new2(r->size);
291 size_t i;
292 unsigned long base = (unsigned long)r->drops;
294 for (i = 0; i < r->size; i++) {
295 rb_ary_push(rv, ULONG2NUM(*((unsigned long *)base)));
296 base += raindrop_size;
299 return rv;
303 * call-seq:
304 * rd.size -> Integer
306 * Returns the number of counters a Raindrops object can hold. Due to
307 * page alignment, this is always equal or greater than the number of
308 * requested slots passed to Raindrops.new
310 static VALUE size(VALUE self)
312 return SIZET2NUM(get(self)->size);
316 * call-seq:
317 * rd[index] = value
319 * Assigns +value+ to the slot designated by +index+
321 static VALUE aset(VALUE self, VALUE index, VALUE value)
323 unsigned long *addr = addr_of(self, index);
325 *addr = NUM2ULONG(value);
327 return value;
331 * call-seq:
332 * rd[index] -> value
334 * Returns the value of the slot designated by +index+
336 static VALUE aref(VALUE self, VALUE index)
338 return ULONG2NUM(*addr_of(self, index));
341 #ifdef __linux__
342 void Init_raindrops_linux_inet_diag(void);
343 void Init_raindrops_linux_tcp_info(void);
344 #endif
346 #ifndef _SC_NPROCESSORS_CONF
347 # if defined _SC_NPROCESSORS_ONLN
348 # define _SC_NPROCESSORS_CONF _SC_NPROCESSORS_ONLN
349 # elif defined _SC_NPROC_ONLN
350 # define _SC_NPROCESSORS_CONF _SC_NPROC_ONLN
351 # elif defined _SC_CRAY_NCPU
352 # define _SC_NPROCESSORS_CONF _SC_CRAY_NCPU
353 # endif
354 #endif
357 * call-seq:
358 * rd.evaporate! -> nil
360 * Releases mmap()-ed memory allocated for the Raindrops object back
361 * to the OS. The Ruby garbage collector will also release memory
362 * automatically when it is not needed, but this forces release
363 * under high memory pressure.
365 static VALUE evaporate_bang(VALUE self)
367 struct raindrops *r = get(self);
368 void *addr = r->drops;
370 r->drops = MAP_FAILED;
371 if (munmap(addr, raindrop_size * r->capa) != 0)
372 rb_sys_fail("munmap");
373 return Qnil;
376 void Init_raindrops_ext(void)
378 VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
379 long tmp = 2;
381 #ifdef _SC_NPROCESSORS_CONF
382 tmp = sysconf(_SC_NPROCESSORS_CONF);
383 #endif
384 /* no point in padding on single CPU machines */
385 if (tmp == 1)
386 raindrop_size = sizeof(unsigned long);
387 #ifdef _SC_LEVEL1_DCACHE_LINESIZE
388 if (tmp != 1) {
389 tmp = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
390 if (tmp > 0)
391 raindrop_size = (size_t)tmp;
393 #endif
394 #if defined(_SC_PAGE_SIZE)
395 rd_page_size = (size_t)sysconf(_SC_PAGE_SIZE);
396 #elif defined(_SC_PAGESIZE)
397 rd_page_size = (size_t)sysconf(_SC_PAGESIZE);
398 #elif defined(HAVE_GETPAGESIZE)
399 rd_page_size = (size_t)getpagesize();
400 #elif defined(PAGE_SIZE)
401 rd_page_size = (size_t)PAGE_SIZE;
402 #elif defined(PAGESIZE)
403 rd_page_size = (size_t)PAGESIZE;
404 #else
405 # error unable to detect page size for mmap()
406 #endif
407 if ((rd_page_size == (size_t)-1) || (rd_page_size < raindrop_size))
408 rb_raise(rb_eRuntimeError,
409 "system page size invalid: %llu",
410 (unsigned long long)rd_page_size);
413 * The size of one page of memory for a mmap()-ed Raindrops region.
414 * Typically 4096 bytes under Linux.
416 rb_define_const(cRaindrops, "PAGE_SIZE", SIZET2NUM(rd_page_size));
419 * The size (in bytes) of a slot in a Raindrops object.
420 * This is the size of a word on single CPU systems and
421 * the size of the L1 cache line size if detectable.
423 * Defaults to 128 bytes if undetectable.
425 rb_define_const(cRaindrops, "SIZE", SIZET2NUM(raindrop_size));
428 * The maximum value a raindrop counter can hold
430 rb_define_const(cRaindrops, "MAX", ULONG2NUM((unsigned long)-1));
432 rb_define_alloc_func(cRaindrops, alloc);
434 rb_define_method(cRaindrops, "initialize", init, 1);
435 rb_define_method(cRaindrops, "incr", incr, -1);
436 rb_define_method(cRaindrops, "decr", decr, -1);
437 rb_define_method(cRaindrops, "to_ary", to_ary, 0);
438 rb_define_method(cRaindrops, "[]", aref, 1);
439 rb_define_method(cRaindrops, "[]=", aset, 2);
440 rb_define_method(cRaindrops, "size", size, 0);
441 rb_define_method(cRaindrops, "size=", setsize, 1);
442 rb_define_method(cRaindrops, "capa", capa, 0);
443 rb_define_method(cRaindrops, "initialize_copy", init_copy, 1);
444 rb_define_method(cRaindrops, "evaporate!", evaporate_bang, 0);
446 #ifdef __linux__
447 Init_raindrops_linux_inet_diag();
448 Init_raindrops_linux_tcp_info();
449 #endif