8 #include "raindrops_atomic.h"
11 # define SIZET2NUM(x) ULONG2NUM(x)
14 # define NUM2SIZET(x) NUM2ULONG(x)
18 * most modern CPUs have a cache-line size of 64 or 128.
19 * We choose a bigger one by default since our structure is not
22 static size_t raindrop_size
= 128;
23 static size_t rd_page_size
;
25 #define PAGE_MASK (~(rd_page_size - 1))
26 #define PAGE_ALIGN(addr) (((addr) + rd_page_size - 1) & PAGE_MASK)
28 /* each raindrop is a counter */
30 unsigned long counter
;
31 } __attribute__((packed
));
33 /* allow mmap-ed regions to store more than one raindrop */
39 struct raindrop
*drops
;
43 static void rd_mark(void *ptr
)
45 struct raindrops
*r
= ptr
;
50 static void rd_free(void *ptr
)
52 struct raindrops
*r
= ptr
;
54 if (r
->drops
!= MAP_FAILED
) {
55 int rv
= munmap(r
->drops
, raindrop_size
* r
->capa
);
57 rb_bug("munmap failed in gc: %s", strerror(errno
));
63 static size_t rd_memsize(const void *ptr
)
65 const struct raindrops
*r
= ptr
;
67 return r
->drops
== MAP_FAILED
? 0 : raindrop_size
* r
->capa
;
70 static const rb_data_type_t rd_type
= {
72 { rd_mark
, rd_free
, rd_memsize
, /* reserved */ },
73 /* parent, data, [ flags ] */
76 /* automatically called at creation (before initialize) */
77 static VALUE
alloc(VALUE klass
)
80 VALUE rv
= TypedData_Make_Struct(klass
, struct raindrops
, &rd_type
, r
);
82 r
->drops
= MAP_FAILED
;
86 static struct raindrops
*get(VALUE self
)
90 TypedData_Get_Struct(self
, struct raindrops
, &rd_type
, r
);
92 if (r
->drops
== MAP_FAILED
)
93 rb_raise(rb_eStandardError
, "invalid or freed Raindrops");
99 * This is the _actual_ implementation of #initialize - the Ruby wrapper
100 * handles keyword-argument handling then calls this method.
102 static VALUE
init_cimpl(VALUE self
, VALUE size
, VALUE io
, VALUE zero
)
104 struct raindrops
*r
= DATA_PTR(self
);
108 if (r
->drops
!= MAP_FAILED
)
109 rb_raise(rb_eRuntimeError
, "already initialized");
111 r
->size
= NUM2SIZET(size
);
113 rb_raise(rb_eArgError
, "size must be >= 1");
115 tmp
= PAGE_ALIGN(raindrop_size
* r
->size
);
116 r
->capa
= tmp
/ raindrop_size
;
117 assert(PAGE_ALIGN(raindrop_size
* r
->capa
) == tmp
&& "not aligned");
123 int fd
= NUM2INT(rb_funcall(r
->io
, rb_intern("fileno"), 0));
124 rb_funcall(r
->io
, rb_intern("truncate"), 1, SIZET2NUM(tmp
));
125 r
->drops
= mmap(NULL
, tmp
,
126 PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
128 r
->drops
= mmap(NULL
, tmp
,
129 PROT_READ
|PROT_WRITE
, MAP_ANON
|MAP_SHARED
,
132 if (r
->drops
== MAP_FAILED
) {
135 if ((err
== EAGAIN
|| err
== ENOMEM
) && tries
-- > 0) {
144 memset(r
->drops
, 0, tmp
);
150 * mremap() is currently broken with MAP_SHARED
151 * https://bugzilla.kernel.org/show_bug.cgi?id=8691
153 #if defined(HAVE_MREMAP) && !defined(MREMAP_WORKS_WITH_MAP_SHARED)
158 #ifndef MREMAP_MAYMOVE
159 # warn MREMAP_MAYMOVE undefined
160 # define MREMAP_MAYMOVE 0
162 static void resize(struct raindrops
*r
, size_t new_rd_size
)
164 size_t old_size
= raindrop_size
* r
->capa
;
165 size_t new_size
= PAGE_ALIGN(raindrop_size
* new_rd_size
);
166 void *old_address
= r
->drops
;
169 if (r
->pid
!= getpid())
170 rb_raise(rb_eRuntimeError
, "cannot mremap() from child");
172 rv
= mremap(old_address
, old_size
, new_size
, MREMAP_MAYMOVE
);
173 if (rv
== MAP_FAILED
) {
176 if (err
== EAGAIN
|| err
== ENOMEM
) {
178 rv
= mremap(old_address
, old_size
, new_size
, 0);
180 if (rv
== MAP_FAILED
)
181 rb_sys_fail("mremap");
184 r
->size
= new_rd_size
;
185 r
->capa
= new_size
/ raindrop_size
;
186 assert(r
->capa
>= r
->size
&& "bad sizing");
188 #else /* ! HAVE_MREMAP */
190 * we cannot use munmap + mmap to reallocate the buffer since it may
191 * already be shared by other processes, so we just fail
193 static void resize(struct raindrops
*r
, size_t new_rd_size
)
195 rb_raise(rb_eRangeError
, "mremap(2) is not available");
197 #endif /* ! HAVE_MREMAP */
203 * Increases or decreases the current capacity of our Raindrop.
204 * Raises RangeError if +new_size+ is too big or small for the
205 * current backing store
207 static VALUE
setsize(VALUE self
, VALUE new_size
)
209 size_t new_rd_size
= NUM2SIZET(new_size
);
210 struct raindrops
*r
= get(self
);
212 if (new_rd_size
<= r
->capa
)
213 r
->size
= new_rd_size
;
215 resize(r
, new_rd_size
);
224 * Returns the number of slots allocated (but not necessarily used) by
225 * the Raindrops object.
227 static VALUE
capa(VALUE self
)
229 return SIZET2NUM(get(self
)->capa
);
236 * Duplicates and snapshots the current state of a Raindrops object. Even
237 * if the given Raindrops object is backed by a file, the copy will be backed
238 * by independent, anonymously mapped memory.
240 static VALUE
init_copy(VALUE dest
, VALUE source
)
242 struct raindrops
*dst
= DATA_PTR(dest
);
243 struct raindrops
*src
= get(source
);
245 init_cimpl(dest
, SIZET2NUM(src
->size
), Qnil
, Qfalse
);
246 memcpy(dst
->drops
, src
->drops
, raindrop_size
* src
->size
);
251 static unsigned long *addr_of(VALUE self
, VALUE index
)
253 struct raindrops
*r
= get(self
);
254 unsigned long off
= FIX2ULONG(index
) * raindrop_size
;
256 if (off
>= raindrop_size
* r
->size
)
257 rb_raise(rb_eArgError
, "offset overrun");
259 return (unsigned long *)((unsigned long)r
->drops
+ off
);
262 static unsigned long incr_decr_arg(int argc
, const VALUE
*argv
)
264 if (argc
> 2 || argc
< 1)
265 rb_raise(rb_eArgError
,
266 "wrong number of arguments (%d for 1+)", argc
);
268 return argc
== 2 ? NUM2ULONG(argv
[1]) : 1;
273 * rd.incr(index[, number]) -> result
275 * Increments the value referred to by the +index+ by +number+.
276 * +number+ defaults to +1+ if unspecified.
278 static VALUE
incr(int argc
, VALUE
*argv
, VALUE self
)
280 unsigned long nr
= incr_decr_arg(argc
, argv
);
282 return ULONG2NUM(__sync_add_and_fetch(addr_of(self
, argv
[0]), nr
));
287 * rd.decr(index[, number]) -> result
289 * Decrements the value referred to by the +index+ by +number+.
290 * +number+ defaults to +1+ if unspecified.
292 static VALUE
decr(int argc
, VALUE
*argv
, VALUE self
)
294 unsigned long nr
= incr_decr_arg(argc
, argv
);
296 return ULONG2NUM(__sync_sub_and_fetch(addr_of(self
, argv
[0]), nr
));
303 * converts the Raindrops structure to an Array
305 static VALUE
to_ary(VALUE self
)
307 struct raindrops
*r
= get(self
);
308 VALUE rv
= rb_ary_new2(r
->size
);
310 unsigned long base
= (unsigned long)r
->drops
;
312 for (i
= 0; i
< r
->size
; i
++) {
313 rb_ary_push(rv
, ULONG2NUM(*((unsigned long *)base
)));
314 base
+= raindrop_size
;
324 * Returns the number of counters a Raindrops object can hold. Due to
325 * page alignment, this is always equal or greater than the number of
326 * requested slots passed to Raindrops.new
328 static VALUE
size(VALUE self
)
330 return SIZET2NUM(get(self
)->size
);
337 * Assigns +value+ to the slot designated by +index+
339 static VALUE
aset(VALUE self
, VALUE index
, VALUE value
)
341 unsigned long *addr
= addr_of(self
, index
);
343 *addr
= NUM2ULONG(value
);
352 * Returns the value of the slot designated by +index+
354 static VALUE
aref(VALUE self
, VALUE index
)
356 return ULONG2NUM(*addr_of(self
, index
));
360 void Init_raindrops_linux_inet_diag(void);
362 #ifdef HAVE_TYPE_STRUCT_TCP_INFO
363 void Init_raindrops_tcp_info(void);
366 #ifndef _SC_NPROCESSORS_CONF
367 # if defined _SC_NPROCESSORS_ONLN
368 # define _SC_NPROCESSORS_CONF _SC_NPROCESSORS_ONLN
369 # elif defined _SC_NPROC_ONLN
370 # define _SC_NPROCESSORS_CONF _SC_NPROC_ONLN
371 # elif defined _SC_CRAY_NCPU
372 # define _SC_NPROCESSORS_CONF _SC_CRAY_NCPU
378 * rd.evaporate! -> nil
380 * Releases mmap()-ed memory allocated for the Raindrops object back
381 * to the OS. The Ruby garbage collector will also release memory
382 * automatically when it is not needed, but this forces release
383 * under high memory pressure.
385 static VALUE
evaporate_bang(VALUE self
)
387 struct raindrops
*r
= get(self
);
388 void *addr
= r
->drops
;
390 r
->drops
= MAP_FAILED
;
391 if (munmap(addr
, raindrop_size
* r
->capa
) != 0)
392 rb_sys_fail("munmap");
400 * Returns the IO object backing the memory for this raindrop, if
401 * one was specified when constructing this Raindrop. If this
402 * Raindrop is backed by anonymous memory, this method returns nil.
404 static VALUE
to_io(VALUE self
)
406 struct raindrops
*r
= get(self
);
410 void Init_raindrops_ext(void)
412 VALUE cRaindrops
= rb_define_class("Raindrops", rb_cObject
);
415 #ifdef _SC_NPROCESSORS_CONF
416 tmp
= sysconf(_SC_NPROCESSORS_CONF
);
418 /* no point in padding on single CPU machines */
420 raindrop_size
= sizeof(unsigned long);
421 #ifdef _SC_LEVEL1_DCACHE_LINESIZE
423 tmp
= sysconf(_SC_LEVEL1_DCACHE_LINESIZE
);
425 raindrop_size
= (size_t)tmp
;
428 #if defined(_SC_PAGE_SIZE)
429 rd_page_size
= (size_t)sysconf(_SC_PAGE_SIZE
);
430 #elif defined(_SC_PAGESIZE)
431 rd_page_size
= (size_t)sysconf(_SC_PAGESIZE
);
432 #elif defined(HAVE_GETPAGESIZE)
433 rd_page_size
= (size_t)getpagesize();
434 #elif defined(PAGE_SIZE)
435 rd_page_size
= (size_t)PAGE_SIZE
;
436 #elif defined(PAGESIZE)
437 rd_page_size
= (size_t)PAGESIZE
;
439 # error unable to detect page size for mmap()
441 if ((rd_page_size
== (size_t)-1) || (rd_page_size
< raindrop_size
))
442 rb_raise(rb_eRuntimeError
,
443 "system page size invalid: %llu",
444 (unsigned long long)rd_page_size
);
447 * The size of one page of memory for a mmap()-ed Raindrops region.
448 * Typically 4096 bytes under Linux.
450 rb_define_const(cRaindrops
, "PAGE_SIZE", SIZET2NUM(rd_page_size
));
453 * The size (in bytes) of a slot in a Raindrops object.
454 * This is the size of a word on single CPU systems and
455 * the size of the L1 cache line size if detectable.
457 * Defaults to 128 bytes if undetectable.
459 rb_define_const(cRaindrops
, "SIZE", SIZET2NUM(raindrop_size
));
462 * The maximum value a raindrop counter can hold
464 rb_define_const(cRaindrops
, "MAX", ULONG2NUM((unsigned long)-1));
466 rb_define_alloc_func(cRaindrops
, alloc
);
468 rb_define_private_method(cRaindrops
, "initialize_cimpl", init_cimpl
, 3);
469 rb_define_method(cRaindrops
, "incr", incr
, -1);
470 rb_define_method(cRaindrops
, "decr", decr
, -1);
471 rb_define_method(cRaindrops
, "to_ary", to_ary
, 0);
472 rb_define_method(cRaindrops
, "[]", aref
, 1);
473 rb_define_method(cRaindrops
, "[]=", aset
, 2);
474 rb_define_method(cRaindrops
, "size", size
, 0);
475 rb_define_method(cRaindrops
, "size=", setsize
, 1);
476 rb_define_method(cRaindrops
, "capa", capa
, 0);
477 rb_define_method(cRaindrops
, "initialize_copy", init_copy
, 1);
478 rb_define_method(cRaindrops
, "evaporate!", evaporate_bang
, 0);
479 rb_define_method(cRaindrops
, "to_io", to_io
, 0);
482 Init_raindrops_linux_inet_diag();
484 #ifdef HAVE_TYPE_STRUCT_TCP_INFO
485 Init_raindrops_tcp_info();