7 #include "raindrops_atomic.h"
10 # define SIZET2NUM(x) ULONG2NUM(x)
13 # define NUM2SIZET(x) NUM2ULONG(x)
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
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 */
29 unsigned long counter
;
30 } __attribute__((packed
));
32 /* allow mmap-ed regions to store more than one raindrop */
37 struct raindrop
*drops
;
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
);
48 rb_bug("munmap failed in gc: %s", strerror(errno
));
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
= {
63 { NULL
, rd_free
, rd_memsize
, /* reserved */ },
64 /* parent, data, [ flags ] */
67 /* automatically called at creation (before initialize) */
68 static VALUE
alloc(VALUE klass
)
71 VALUE rv
= TypedData_Make_Struct(klass
, struct raindrops
, &rd_type
, r
);
73 r
->drops
= MAP_FAILED
;
77 static struct raindrops
*get(VALUE self
)
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");
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
);
105 if (r
->drops
!= MAP_FAILED
)
106 rb_raise(rb_eRuntimeError
, "already initialized");
108 r
->size
= NUM2SIZET(size
);
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");
117 r
->drops
= mmap(NULL
, tmp
,
118 PROT_READ
|PROT_WRITE
, MAP_ANON
|MAP_SHARED
, -1, 0);
119 if (r
->drops
== MAP_FAILED
) {
122 if ((err
== EAGAIN
|| err
== ENOMEM
) && tries
-- > 0) {
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)
142 #ifndef MREMAP_MAYMOVE
143 # warn MREMAP_MAYMOVE undefined
144 # define MREMAP_MAYMOVE 0
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
;
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
) {
160 if (err
== EAGAIN
|| err
== ENOMEM
) {
162 rv
= mremap(old_address
, old_size
, new_size
, 0);
164 if (rv
== MAP_FAILED
)
165 rb_sys_fail("mremap");
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 */
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
;
199 resize(r
, new_rd_size
);
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
);
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
);
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;
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
));
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
));
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
);
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
;
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
);
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
);
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
));
342 void Init_raindrops_linux_inet_diag(void);
343 void Init_raindrops_linux_tcp_info(void);
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
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");
376 void Init_raindrops_ext(void)
378 VALUE cRaindrops
= rb_define_class("Raindrops", rb_cObject
);
381 #ifdef _SC_NPROCESSORS_CONF
382 tmp
= sysconf(_SC_NPROCESSORS_CONF
);
384 /* no point in padding on single CPU machines */
386 raindrop_size
= sizeof(unsigned long);
387 #ifdef _SC_LEVEL1_DCACHE_LINESIZE
389 tmp
= sysconf(_SC_LEVEL1_DCACHE_LINESIZE
);
391 raindrop_size
= (size_t)tmp
;
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
;
405 # error unable to detect page size for mmap()
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);
447 Init_raindrops_linux_inet_diag();
448 Init_raindrops_linux_tcp_info();