initial
[raindrops.git] / ext / raindrops / raindrops.c
blob65e3947679d94298d221b201606e38cd0ce33dea
1 #include <ruby.h>
2 #include <sys/mman.h>
3 #include <assert.h>
4 #include <errno.h>
5 #include <stddef.h>
7 /*
8 * most modern CPUs have a cache-line size of 64 or 128.
9 * We choose a bigger one by default since our structure is not
10 * heavily used
12 #ifndef CACHE_LINE_SIZE
13 # define CACHE_LINE_SIZE 128
14 #endif
16 /* each raindrop is a counter */
17 struct raindrop {
18 union {
19 unsigned long counter;
20 unsigned char padding[CACHE_LINE_SIZE];
21 } as;
22 } __attribute__((packed));
24 /* allow mmap-ed regions can store more than one raindrop */
25 struct raindrops {
26 long size;
27 struct raindrop *drops;
30 /* called by GC */
31 static void evaporate(void *ptr)
33 struct raindrops *r = ptr;
35 if (r->drops) {
36 int rv = munmap(r->drops, sizeof(struct raindrop) * r->size);
37 if (rv != 0)
38 rb_bug("munmap failed in gc: %s", strerror(errno));
41 xfree(ptr);
44 /* automatically called at creation (before initialize) */
45 static VALUE alloc(VALUE klass)
47 struct raindrops *r;
49 return Data_Make_Struct(klass, struct raindrops, NULL, evaporate, r);
52 static struct raindrops *get(VALUE self)
54 struct raindrops *r;
56 Data_Get_Struct(self, struct raindrops, r);
58 return r;
61 /* initializes a Raindrops object to hold +size+ elements */
62 static VALUE init(VALUE self, VALUE size)
64 struct raindrops *r = get(self);
65 int tries = 1;
67 if (r->drops)
68 rb_raise(rb_eRuntimeError, "already initialized");
70 r->size = NUM2LONG(size);
71 if (r->size < 1)
72 rb_raise(rb_eArgError, "size must be >= 1");
74 retry:
75 r->drops = mmap(NULL, sizeof(struct raindrop) * r->size,
76 PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
77 if (r->drops == MAP_FAILED) {
78 if ((errno == EAGAIN || errno == ENOMEM) && tries-- > 0) {
79 rb_gc();
80 goto retry;
82 rb_sys_fail("mmap");
85 return self;
88 /* :nodoc */
89 static VALUE init_copy(VALUE dest, VALUE source)
91 struct raindrops *dst = get(dest);
92 struct raindrops *src = get(source);
94 init(dest, LONG2NUM(src->size));
95 memcpy(dst->drops, src->drops, sizeof(struct raindrop) * src->size);
97 return dest;
100 static unsigned long *addr_of(VALUE self, VALUE index)
102 struct raindrops *r = get(self);
103 unsigned long off = FIX2ULONG(index) * sizeof(struct raindrop);
105 if (off >= sizeof(struct raindrop) * r->size)
106 rb_raise(rb_eArgError, "offset overrun");
108 return (unsigned long *)((unsigned long)r->drops + off);
111 static unsigned long incr_decr_arg(int argc, const VALUE *argv)
113 if (argc > 2 || argc < 1)
114 rb_raise(rb_eArgError,
115 "wrong number of arguments (%d for 1+)", argc);
117 return argc == 2 ? NUM2ULONG(argv[1]) : 1;
120 /* increments the value referred to by the +index+ constant by 1 */
121 static VALUE incr(int argc, VALUE *argv, VALUE self)
123 unsigned long nr = incr_decr_arg(argc, argv);
125 return ULONG2NUM(__sync_add_and_fetch(addr_of(self, argv[0]), nr));
128 /* decrements the value referred to by the +index+ constant by 1 */
129 static VALUE decr(int argc, VALUE *argv, VALUE self)
131 unsigned long nr = incr_decr_arg(argc, argv);
133 return ULONG2NUM(__sync_sub_and_fetch(addr_of(self, argv[0]), nr));
136 /* converts the raindrops structure to an Array */
137 static VALUE to_ary(VALUE self)
139 struct raindrops *r = get(self);
140 VALUE rv = rb_ary_new2(r->size);
141 long i;
142 unsigned long base = (unsigned long)r->drops;
144 for (i = 0; i < r->size; i++) {
145 rb_ary_push(rv, ULONG2NUM(*((unsigned long *)base)));
146 base += sizeof(struct raindrop);
149 return rv;
152 static VALUE size(VALUE self)
154 return LONG2NUM(get(self)->size);
157 static VALUE aset(VALUE self, VALUE index, VALUE value)
159 unsigned long *addr = addr_of(self, index);
161 *addr = NUM2ULONG(value);
163 return value;
166 static VALUE aref(VALUE self, VALUE index)
168 return ULONG2NUM(*addr_of(self, index));
171 #ifdef __linux__
172 void Init_raindrops_linux_inet_diag(void);
173 #endif
175 void Init_raindrops_ext(void)
177 VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
178 rb_define_alloc_func(cRaindrops, alloc);
180 rb_define_method(cRaindrops, "initialize", init, 1);
181 rb_define_method(cRaindrops, "incr", incr, -1);
182 rb_define_method(cRaindrops, "decr", decr, -1);
183 rb_define_method(cRaindrops, "to_ary", to_ary, 0);
184 rb_define_method(cRaindrops, "[]", aref, 1);
185 rb_define_method(cRaindrops, "[]=", aset, 2);
186 rb_define_method(cRaindrops, "size", size, 0);
187 rb_define_method(cRaindrops, "initialize_copy", init_copy, 1);
189 #ifdef __linux__
190 Init_raindrops_linux_inet_diag();
191 #endif