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 gcfree(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 /* automatically called at creation (before initialize) */
55 static VALUE
alloc(VALUE klass
)
58 VALUE rv
= Data_Make_Struct(klass
, struct raindrops
, NULL
, gcfree
, r
);
60 r
->drops
= MAP_FAILED
;
64 static struct raindrops
*get(VALUE self
)
68 Data_Get_Struct(self
, struct raindrops
, r
);
70 if (r
->drops
== MAP_FAILED
)
71 rb_raise(rb_eStandardError
, "invalid or freed Raindrops");
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
);
92 if (r
->drops
!= MAP_FAILED
)
93 rb_raise(rb_eRuntimeError
, "already initialized");
95 r
->size
= NUM2SIZET(size
);
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");
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) {
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)
127 #ifndef MREMAP_MAYMOVE
128 # warn MREMAP_MAYMOVE undefined
129 # define MREMAP_MAYMOVE 0
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
;
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
) {
145 rv
= mremap(old_address
, old_size
, new_size
, 0);
147 if (rv
== MAP_FAILED
)
148 rb_sys_fail("mremap");
151 r
->size
= new_rd_size
;
152 r
->capa
= new_size
/ raindrop_size
;
153 assert(r
->capa
>= 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 */
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
;
182 resize(r
, new_rd_size
);
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
);
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
, SIZET2NUM(src
->size
));
211 memcpy(dst
->drops
, src
->drops
, raindrop_size
* src
->size
);
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;
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
));
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
));
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
);
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
;
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 SIZET2NUM(get(self
)->size
);
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
);
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
));
325 void Init_raindrops_linux_inet_diag(void);
326 void Init_raindrops_linux_tcp_info(void);
329 #ifndef _SC_NPROCESSORS_CONF
330 # if defined _SC_NPROCESSORS_ONLN
331 # define _SC_NPROCESSORS_CONF _SC_NPROCESSORS_ONLN
332 # elif defined _SC_NPROC_ONLN
333 # define _SC_NPROCESSORS_CONF _SC_NPROC_ONLN
334 # elif defined _SC_CRAY_NCPU
335 # define _SC_NPROCESSORS_CONF _SC_CRAY_NCPU
341 * rd.evaporate! -> nil
343 * Releases mmap()-ed memory allocated for the Raindrops object back
344 * to the OS. The Ruby garbage collector will also release memory
345 * automatically when it is not needed, but this forces release
346 * under high memory pressure.
348 static VALUE
evaporate_bang(VALUE self
)
350 struct raindrops
*r
= get(self
);
351 void *addr
= r
->drops
;
353 r
->drops
= MAP_FAILED
;
354 if (munmap(addr
, raindrop_size
* r
->capa
) != 0)
355 rb_sys_fail("munmap");
359 void Init_raindrops_ext(void)
361 VALUE cRaindrops
= rb_define_class("Raindrops", rb_cObject
);
364 #ifdef _SC_NPROCESSORS_CONF
365 tmp
= sysconf(_SC_NPROCESSORS_CONF
);
367 /* no point in padding on single CPU machines */
369 raindrop_size
= sizeof(unsigned long);
370 #ifdef _SC_LEVEL1_DCACHE_LINESIZE
372 tmp
= sysconf(_SC_LEVEL1_DCACHE_LINESIZE
);
374 raindrop_size
= (size_t)tmp
;
377 #if defined(_SC_PAGE_SIZE)
378 rd_page_size
= (size_t)sysconf(_SC_PAGE_SIZE
);
379 #elif defined(_SC_PAGESIZE)
380 rd_page_size
= (size_t)sysconf(_SC_PAGESIZE
);
381 #elif defined(HAVE_GETPAGESIZE)
382 rd_page_size
= (size_t)getpagesize();
383 #elif defined(PAGE_SIZE)
384 rd_page_size
= (size_t)PAGE_SIZE
;
385 #elif defined(PAGESIZE)
386 rd_page_size
= (size_t)PAGESIZE
;
388 # error unable to detect page size for mmap()
390 if ((rd_page_size
== (size_t)-1) || (rd_page_size
< raindrop_size
))
391 rb_raise(rb_eRuntimeError
,
392 "system page size invalid: %llu",
393 (unsigned long long)rd_page_size
);
396 * The size of one page of memory for a mmap()-ed Raindrops region.
397 * Typically 4096 bytes under Linux.
399 rb_define_const(cRaindrops
, "PAGE_SIZE", SIZET2NUM(rd_page_size
));
402 * The size (in bytes) of a slot in a Raindrops object.
403 * This is the size of a word on single CPU systems and
404 * the size of the L1 cache line size if detectable.
406 * Defaults to 128 bytes if undetectable.
408 rb_define_const(cRaindrops
, "SIZE", SIZET2NUM(raindrop_size
));
411 * The maximum value a raindrop counter can hold
413 rb_define_const(cRaindrops
, "MAX", ULONG2NUM((unsigned long)-1));
415 rb_define_alloc_func(cRaindrops
, alloc
);
417 rb_define_method(cRaindrops
, "initialize", init
, 1);
418 rb_define_method(cRaindrops
, "incr", incr
, -1);
419 rb_define_method(cRaindrops
, "decr", decr
, -1);
420 rb_define_method(cRaindrops
, "to_ary", to_ary
, 0);
421 rb_define_method(cRaindrops
, "[]", aref
, 1);
422 rb_define_method(cRaindrops
, "[]=", aset
, 2);
423 rb_define_method(cRaindrops
, "size", size
, 0);
424 rb_define_method(cRaindrops
, "size=", setsize
, 1);
425 rb_define_method(cRaindrops
, "capa", capa
, 0);
426 rb_define_method(cRaindrops
, "initialize_copy", init_copy
, 1);
427 rb_define_method(cRaindrops
, "evaporate!", evaporate_bang
, 0);
430 Init_raindrops_linux_inet_diag();
431 Init_raindrops_linux_tcp_info();