Class: Raindrops

Inherits:
Object
  • Object
show all
Defined in:
lib/raindrops.rb,
ext/raindrops/raindrops.c

Overview

Each Raindrops object is a container that holds several counters. It is internally a page-aligned, shared memory area that allows atomic increments, decrements, assignments and reads without any locking.

rd = Raindrops.new 4
rd.incr(0, 1)   -> 1
rd.to_ary       -> [ 1, 0, 0, 0 ]

Unlike many classes in this package, the core Raindrops class is intended to be portable to all reasonably modern *nix systems supporting mmap(). Please let us know if you have portability issues, patches or pull requests at [email protected]

Defined Under Namespace

Modules: Aggregate, Linux Classes: InetDiagSocket, LastDataRecv, ListenStats, Middleware, Struct, TCP_Info, Watcher

Constant Summary collapse

PAGE_SIZE =

The size of one page of memory for a mmap()-ed Raindrops region. Typically 4096 bytes under Linux.

SIZET2NUM(rd_page_size)
SIZE =

The size (in bytes) of a slot in a Raindrops object. This is the size of a word on single CPU systems and the size of the L1 cache line size if detectable.

Defaults to 128 bytes if undetectable.

SIZET2NUM(raindrop_size)
MAX =

The maximum value a raindrop counter can hold

ULONG2NUM((unsigned long)-1)

Instance Method Summary collapse

Constructor Details

#initialize(size) ⇒ Object

Raindrops.new(size) -> raindrops object

Initializes a Raindrops object to hold size counters. size is only a hint and the actual number of counters the object has is dependent on the CPU model, number of cores, and page size of the machine. The actual size of the object will always be equal or greater than the specified size.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'ext/raindrops/raindrops.c', line 86

static VALUE init(VALUE self, VALUE size)
{
	struct raindrops *r = DATA_PTR(self);
	int tries = 1;
	size_t tmp;

	if (r->drops != MAP_FAILED)
		rb_raise(rb_eRuntimeError, "already initialized");

	r->size = NUM2SIZET(size);
	if (r->size < 1)
		rb_raise(rb_eArgError, "size must be >= 1");

	tmp = PAGE_ALIGN(raindrop_size * r->size);
	r->capa = tmp / raindrop_size;
	assert(PAGE_ALIGN(raindrop_size * r->capa) == tmp && "not aligned");

retry:
	r->drops = mmap(NULL, tmp,
	                PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
	if (r->drops == MAP_FAILED) {
		if ((errno == EAGAIN || errno == ENOMEM) && tries-- > 0) {
			rb_gc();
			goto retry;
		}
		rb_sys_fail("mmap");
	}
	r->pid = getpid();

	return self;
}

Instance Method Details

#[](index) ⇒ Object

rd -> value

Returns the value of the slot designated by index



319
320
321
322
# File 'ext/raindrops/raindrops.c', line 319

static VALUE aref(VALUE self, VALUE index)
{
	return  ULONG2NUM(*addr_of(self, index));
}

#[]=(index, value) ⇒ Object

rd = value

Assigns value to the slot designated by index



304
305
306
307
308
309
310
311
# File 'ext/raindrops/raindrops.c', line 304

static VALUE aset(VALUE self, VALUE index, VALUE value)
{
	unsigned long *addr = addr_of(self, index);

	*addr = NUM2ULONG(value);

	return value;
}

#capaObject

rd.capa -> Integer

Returns the number of slots allocated (but not necessarily used) by the Raindrops object.



194
195
196
197
# File 'ext/raindrops/raindrops.c', line 194

static VALUE capa(VALUE self)
{
	return SIZET2NUM(get(self)->capa);
}

#decr(*args) ⇒ Object

rd.decr(index[, number]) -> result

Decrements the value referred to by the index by number. number defaults to 1 if unspecified.



257
258
259
260
261
262
# File 'ext/raindrops/raindrops.c', line 257

static VALUE decr(int argc, VALUE *argv, VALUE self)
{
	unsigned long nr = incr_decr_arg(argc, argv);

	return ULONG2NUM(__sync_sub_and_fetch(addr_of(self, argv[0]), nr));
}

#evaporate!Object

rd.evaporate! -> nil

Releases mmap()-ed memory allocated for the Raindrops object back to the OS. The Ruby garbage collector will also release memory automatically when it is not needed, but this forces release under high memory pressure.



346
347
348
349
350
351
352
353
354
355
# File 'ext/raindrops/raindrops.c', line 346

static VALUE evaporate_bang(VALUE self)
{
	struct raindrops *r = get(self);
	void *addr = r->drops;

	r->drops = MAP_FAILED;
	if (munmap(addr, raindrop_size * r->capa) != 0)
		rb_sys_fail("munmap");
	return Qnil;
}

#incr(*args) ⇒ Object

rd.incr(index[, number]) -> result

Increments the value referred to by the index by number. number defaults to 1 if unspecified.



243
244
245
246
247
248
# File 'ext/raindrops/raindrops.c', line 243

static VALUE incr(int argc, VALUE *argv, VALUE self)
{
	unsigned long nr = incr_decr_arg(argc, argv);

	return ULONG2NUM(__sync_add_and_fetch(addr_of(self, argv[0]), nr));
}

#initialize_copy(source) ⇒ Object

rd.dup -> rd_copy

Duplicates and snapshots the current state of a Raindrops object.



205
206
207
208
209
210
211
212
213
214
# File 'ext/raindrops/raindrops.c', line 205

static VALUE init_copy(VALUE dest, VALUE source)
{
	struct raindrops *dst = DATA_PTR(dest);
	struct raindrops *src = get(source);

	init(dest, SIZET2NUM(src->size));
	memcpy(dst->drops, src->drops, raindrop_size * src->size);

	return dest;
}

#sizeObject

rd.size -> Integer

Returns the number of counters a Raindrops object can hold. Due to page alignment, this is always equal or greater than the number of requested slots passed to Raindrops.new



293
294
295
296
# File 'ext/raindrops/raindrops.c', line 293

static VALUE size(VALUE self)
{
	return SIZET2NUM(get(self)->size);
}

#size=(new_size) ⇒ Object

rd.size = new_size

Increases or decreases the current capacity of our Raindrop. Raises RangeError if new_size is too big or small for the current backing store



174
175
176
177
178
179
180
181
182
183
184
185
# File 'ext/raindrops/raindrops.c', line 174

static VALUE setsize(VALUE self, VALUE new_size)
{
	size_t new_rd_size = NUM2SIZET(new_size);
	struct raindrops *r = get(self);

	if (new_rd_size <= r->capa)
		r->size = new_rd_size;
	else
		resize(r, new_rd_size);

	return new_size;
}

#to_aryObject

rd.to_ary -> Array

converts the Raindrops structure to an Array



270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'ext/raindrops/raindrops.c', line 270

static VALUE to_ary(VALUE self)
{
	struct raindrops *r = get(self);
	VALUE rv = rb_ary_new2(r->size);
	size_t i;
	unsigned long base = (unsigned long)r->drops;

	for (i = 0; i < r->size; i++) {
		rb_ary_push(rv, ULONG2NUM(*((unsigned long *)base)));
		base += raindrop_size;
	}

	return rv;
}