7 #include "raindrops_atomic.h"
10 # define SIZET2NUM(x) ULONG2NUM(x)
14 * most modern CPUs have a cache-line size of 64 or 128.
15 * We choose a bigger one by default since our structure is not
18 static size_t raindrop_size
= 128;
20 /* each raindrop is a counter */
22 unsigned long counter
;
23 } __attribute__((packed
));
25 /* allow mmap-ed regions to store more than one raindrop */
28 struct raindrop
*drops
;
32 static void evaporate(void *ptr
)
34 struct raindrops
*r
= ptr
;
37 int rv
= munmap(r
->drops
, raindrop_size
* r
->size
);
39 rb_bug("munmap failed in gc: %s", strerror(errno
));
45 /* automatically called at creation (before initialize) */
46 static VALUE
alloc(VALUE klass
)
50 return Data_Make_Struct(klass
, struct raindrops
, NULL
, evaporate
, r
);
53 static struct raindrops
*get(VALUE self
)
57 Data_Get_Struct(self
, struct raindrops
, r
);
64 * Raindrops.new(size) -> raindrops object
66 * Initializes a Raindrops object to hold +size+ counters. +size+ is
67 * only a hint and the actual number of counters the object has is
68 * dependent on the CPU model, number of cores, and page size of
69 * the machine. The actual size of the object will always be equal
70 * or greater than the specified +size+.
72 static VALUE
init(VALUE self
, VALUE size
)
74 struct raindrops
*r
= get(self
);
78 rb_raise(rb_eRuntimeError
, "already initialized");
80 r
->size
= NUM2LONG(size
);
82 rb_raise(rb_eArgError
, "size must be >= 1");
85 r
->drops
= mmap(NULL
, raindrop_size
* r
->size
,
86 PROT_READ
|PROT_WRITE
, MAP_ANON
|MAP_SHARED
, -1, 0);
87 if (r
->drops
== MAP_FAILED
) {
89 if ((errno
== EAGAIN
|| errno
== ENOMEM
) && tries
-- > 0) {
103 * Duplicates and snapshots the current state of a Raindrops object.
105 static VALUE
init_copy(VALUE dest
, VALUE source
)
107 struct raindrops
*dst
= get(dest
);
108 struct raindrops
*src
= get(source
);
110 init(dest
, LONG2NUM(src
->size
));
111 memcpy(dst
->drops
, src
->drops
, raindrop_size
* src
->size
);
116 static unsigned long *addr_of(VALUE self
, VALUE index
)
118 struct raindrops
*r
= get(self
);
119 unsigned long off
= FIX2ULONG(index
) * raindrop_size
;
121 if (off
>= raindrop_size
* r
->size
)
122 rb_raise(rb_eArgError
, "offset overrun");
124 return (unsigned long *)((unsigned long)r
->drops
+ off
);
127 static unsigned long incr_decr_arg(int argc
, const VALUE
*argv
)
129 if (argc
> 2 || argc
< 1)
130 rb_raise(rb_eArgError
,
131 "wrong number of arguments (%d for 1+)", argc
);
133 return argc
== 2 ? NUM2ULONG(argv
[1]) : 1;
138 * rd.incr(index[, number]) -> result
140 * Increments the value referred to by the +index+ by +number+.
141 * +number+ defaults to +1+ if unspecified.
143 static VALUE
incr(int argc
, VALUE
*argv
, VALUE self
)
145 unsigned long nr
= incr_decr_arg(argc
, argv
);
147 return ULONG2NUM(__sync_add_and_fetch(addr_of(self
, argv
[0]), nr
));
152 * rd.decr(index[, number]) -> result
154 * Decrements the value referred to by the +index+ by +number+.
155 * +number+ defaults to +1+ if unspecified.
157 static VALUE
decr(int argc
, VALUE
*argv
, VALUE self
)
159 unsigned long nr
= incr_decr_arg(argc
, argv
);
161 return ULONG2NUM(__sync_sub_and_fetch(addr_of(self
, argv
[0]), nr
));
168 * converts the Raindrops structure to an Array
170 static VALUE
to_ary(VALUE self
)
172 struct raindrops
*r
= get(self
);
173 VALUE rv
= rb_ary_new2(r
->size
);
175 unsigned long base
= (unsigned long)r
->drops
;
177 for (i
= 0; i
< r
->size
; i
++) {
178 rb_ary_push(rv
, ULONG2NUM(*((unsigned long *)base
)));
179 base
+= raindrop_size
;
189 * Returns the number of counters a Raindrops object can hold. Due to
190 * page alignment, this is always equal or greater than the number of
191 * requested slots passed to Raindrops.new
193 static VALUE
size(VALUE self
)
195 return LONG2NUM(get(self
)->size
);
202 * Assigns +value+ to the slot designated by +index+
204 static VALUE
aset(VALUE self
, VALUE index
, VALUE value
)
206 unsigned long *addr
= addr_of(self
, index
);
208 *addr
= NUM2ULONG(value
);
217 * Returns the value of the slot designated by +index+
219 static VALUE
aref(VALUE self
, VALUE index
)
221 return ULONG2NUM(*addr_of(self
, index
));
225 void Init_raindrops_linux_inet_diag(void);
226 void Init_raindrops_linux_tcp_info(void);
229 #ifndef _SC_NPROCESSORS_ONLN
230 # ifdef _SC_NPROC_ONLN
231 # define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
232 # elif defined _SC_CRAY_NCPU
233 # define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
237 void Init_raindrops_ext(void)
239 VALUE cRaindrops
= rb_define_class("Raindrops", rb_cObject
);
242 #ifdef _SC_NPROCESSORS_ONLN
243 tmp
= sysconf(_SC_NPROCESSORS_ONLN
);
245 /* no point in padding on single CPU machines */
247 raindrop_size
= sizeof(unsigned long);
248 #ifdef _SC_LEVEL1_DCACHE_LINESIZE
250 tmp
= sysconf(_SC_LEVEL1_DCACHE_LINESIZE
);
252 raindrop_size
= (size_t)tmp
;
257 * The size (in bytes) of a slot in a Raindrops object.
258 * This is the size of a word on single CPU systems and
259 * the size of the L1 cache line size if detectable.
261 * Defaults to 128 bytes if undetectable.
263 rb_define_const(cRaindrops
, "SIZE", SIZET2NUM(raindrop_size
));
266 * The maximum value a raindrop counter can hold
268 rb_define_const(cRaindrops
, "MAX", ULONG2NUM((unsigned long)-1));
270 rb_define_alloc_func(cRaindrops
, alloc
);
272 rb_define_method(cRaindrops
, "initialize", init
, 1);
273 rb_define_method(cRaindrops
, "incr", incr
, -1);
274 rb_define_method(cRaindrops
, "decr", decr
, -1);
275 rb_define_method(cRaindrops
, "to_ary", to_ary
, 0);
276 rb_define_method(cRaindrops
, "[]", aref
, 1);
277 rb_define_method(cRaindrops
, "[]=", aset
, 2);
278 rb_define_method(cRaindrops
, "size", size
, 0);
279 rb_define_method(cRaindrops
, "initialize_copy", init_copy
, 1);
282 Init_raindrops_linux_inet_diag();
283 Init_raindrops_linux_tcp_info();