xbad 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/ext/xbad/extconf.rb +2 -0
- data/ext/xbad/xbad.c +64 -0
- data/ext/xbad/xbad.h +273 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cb7e5bad134097caa0775a4100841be7801c0089
|
4
|
+
data.tar.gz: 7289023dc5afec82225737848332f6189e792915
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bdfa4fafab6f724b0b727c50908ec11ccb217f33b18f4f4c0a1b247722549358e1a7b406c241ba7eaa47ce0293d0e02f64c435fbd38e38321e3edb1c5a628d0e
|
7
|
+
data.tar.gz: cc644efaf7dc9c0c4fa2e5ed21993b2fa70c25246104a478c927ad3653ee674d2f7169e9099f54d4342ac4c33a12ff4767bffb05db9294765b9b9677c0c7f384
|
data/ext/xbad/extconf.rb
ADDED
data/ext/xbad/xbad.c
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#define BAD_DIE(fmt,arg...) rb_raise(rb_eArgError,fmt,##arg)
|
3
|
+
|
4
|
+
#include "xbad.h"
|
5
|
+
|
6
|
+
|
7
|
+
static VALUE XBAD_init(VALUE class, VALUE _shmid, VALUE _hash_bits, VALUE _item_size ) {
|
8
|
+
int shmid = NUM2INT(_shmid);
|
9
|
+
int item_size = NUM2INT(_item_size);
|
10
|
+
int hash_bits = NUM2INT(_hash_bits);
|
11
|
+
struct shared_pool *p = shared_pool_init(shmid,hash_bits,item_size);
|
12
|
+
VALUE sp = Data_Wrap_Struct(class, 0, shared_pool_destroy, p);
|
13
|
+
return sp;
|
14
|
+
}
|
15
|
+
|
16
|
+
inline struct shared_pool *shared_pool(VALUE obj) {
|
17
|
+
struct shared_pool *sp;
|
18
|
+
Data_Get_Struct(obj,struct shared_pool, sp);
|
19
|
+
return sp;
|
20
|
+
}
|
21
|
+
|
22
|
+
static VALUE XBAD_store(VALUE self, VALUE key, VALUE value, VALUE _expire_after) {
|
23
|
+
struct shared_pool *sp = shared_pool(self);
|
24
|
+
int expire_after = NUM2INT(_expire_after);
|
25
|
+
if (t_add(sp,RSTRING_PTR(key),RSTRING_LEN(key),RSTRING_PTR(value),RSTRING_LEN(value),expire_after) != 0)
|
26
|
+
rb_raise(rb_eArgError,"failed to store");
|
27
|
+
return value;
|
28
|
+
}
|
29
|
+
|
30
|
+
static VALUE XBAD_store_and_broadcast(VALUE self, VALUE key, VALUE value, VALUE _expire_after,VALUE _port) {
|
31
|
+
struct shared_pool *sp = shared_pool(self);
|
32
|
+
int expire_after = NUM2INT(_expire_after);
|
33
|
+
unsigned short port = NUM2INT(_port);
|
34
|
+
struct in_addr ip = { .s_addr = htonl(0xffffffff) };
|
35
|
+
|
36
|
+
if (t_add_and_sent_to(sp,RSTRING_PTR(key),RSTRING_LEN(key),RSTRING_PTR(value),RSTRING_LEN(value),expire_after,ip,port) != 0)
|
37
|
+
rb_raise(rb_eArgError,"failed to store and broadcast");
|
38
|
+
return value;
|
39
|
+
}
|
40
|
+
|
41
|
+
static VALUE XBAD_find(VALUE self, VALUE key) {
|
42
|
+
VALUE ret = Qnil;
|
43
|
+
struct shared_pool *sp = shared_pool(self);
|
44
|
+
struct item *item = t_find_and_lock(sp,RSTRING_PTR(key),RSTRING_LEN(key));
|
45
|
+
if (item) {
|
46
|
+
ret = rb_str_new(ITEM_BLOB(item),item->blob_len);
|
47
|
+
shared_pool_unlock_item(item);
|
48
|
+
}
|
49
|
+
return ret;
|
50
|
+
}
|
51
|
+
|
52
|
+
static VALUE XBAD_reset(VALUE self) {
|
53
|
+
struct shared_pool *sp = shared_pool(self);
|
54
|
+
shared_pool_reset(sp);
|
55
|
+
return Qtrue;
|
56
|
+
}
|
57
|
+
|
58
|
+
void Init_xbad() {
|
59
|
+
VALUE c = rb_define_class("XBAD", rb_cObject);
|
60
|
+
rb_define_singleton_method(c, "create", RUBY_METHOD_FUNC(XBAD_init), 3);
|
61
|
+
rb_define_method(c, "find", XBAD_find, 1);
|
62
|
+
rb_define_method(c, "store", XBAD_store, 3);
|
63
|
+
rb_define_method(c, "store_and_broadcast", XBAD_store_and_broadcast, 4);
|
64
|
+
}
|
data/ext/xbad/xbad.h
ADDED
@@ -0,0 +1,273 @@
|
|
1
|
+
#include <time.h>
|
2
|
+
#include <stdio.h>
|
3
|
+
#include <stdint.h>
|
4
|
+
#include <errno.h>
|
5
|
+
#include <stdlib.h>
|
6
|
+
#include <string.h>
|
7
|
+
#include <sys/shm.h>
|
8
|
+
#include <sys/socket.h>
|
9
|
+
#include <netinet/in.h>
|
10
|
+
#include <unistd.h>
|
11
|
+
#ifndef BAD_DIE
|
12
|
+
#define BAD_DIE(fmt,arg...) \
|
13
|
+
do { \
|
14
|
+
E(fmt,##arg); \
|
15
|
+
exit(EXIT_FAILURE); \
|
16
|
+
} while(0)
|
17
|
+
#endif
|
18
|
+
|
19
|
+
#define FORMAT(fmt,arg...) fmt " [%s():%s:%d @ %u]\n",##arg,__func__,__FILE__,__LINE__,(unsigned int) time(NULL)
|
20
|
+
#define E(fmt,arg...) fprintf(stderr,FORMAT(fmt,##arg))
|
21
|
+
#define D(fmt,arg...) printf(FORMAT(fmt,##arg))
|
22
|
+
#define SAYX(fmt,arg...) do { \
|
23
|
+
BAD_DIE(fmt,##arg); \
|
24
|
+
} while(0)
|
25
|
+
|
26
|
+
#define SAYPX(fmt,arg...) SAYX(fmt " { %s(%d) }",##arg,errno ? strerror(errno) : "undefined error",errno);
|
27
|
+
|
28
|
+
#define ITEM(sp,index) (struct item *) (&(sp)->pool[(index * (sp)->item_size)])
|
29
|
+
#define ITEM_KEY(item) &(item)->blob[0]
|
30
|
+
#define ITEM_BLOB(item) &(item)->blob[(item)->key_len]
|
31
|
+
|
32
|
+
#define FNV_PRIME_32 16777619
|
33
|
+
#define FNV_OFFSET_32 2166136261U
|
34
|
+
|
35
|
+
#ifndef BAD_ALLOC
|
36
|
+
#define BAD_ALLOC(s,cast) \
|
37
|
+
do { \
|
38
|
+
s = (cast *) x_malloc(sizeof(cast)); \
|
39
|
+
} while(0)
|
40
|
+
|
41
|
+
static void *x_malloc(size_t s) {
|
42
|
+
void *x = malloc(s);
|
43
|
+
if (!x)
|
44
|
+
SAYPX("malloc for %zu failed",s);
|
45
|
+
return x;
|
46
|
+
}
|
47
|
+
|
48
|
+
#endif
|
49
|
+
|
50
|
+
#ifndef BAD_FREE
|
51
|
+
#define BAD_FREE(s) x_free(s)
|
52
|
+
|
53
|
+
static void x_free(void *x) {
|
54
|
+
if (x != NULL)
|
55
|
+
free(x);
|
56
|
+
}
|
57
|
+
#endif
|
58
|
+
|
59
|
+
struct item {
|
60
|
+
volatile int lock;
|
61
|
+
uint32_t key_len;
|
62
|
+
uint32_t blob_len;
|
63
|
+
uint32_t expire_at;
|
64
|
+
uint8_t blob[0];
|
65
|
+
} __attribute__((packed));
|
66
|
+
|
67
|
+
struct shared_pool {
|
68
|
+
uint8_t *pool;
|
69
|
+
uint32_t item_size;
|
70
|
+
int shm_id;
|
71
|
+
int key;
|
72
|
+
int hash_mask;
|
73
|
+
int hash_shift;
|
74
|
+
int c_sock;
|
75
|
+
int s_sock;
|
76
|
+
};
|
77
|
+
|
78
|
+
inline uint32_t FNV32(struct shared_pool *sp, const char *s,size_t len) {
|
79
|
+
uint32_t hash = FNV_OFFSET_32, i;
|
80
|
+
for(i = 0; i < len; i++) {
|
81
|
+
hash = hash ^ (s[i]);
|
82
|
+
hash = hash * FNV_PRIME_32;
|
83
|
+
}
|
84
|
+
return (hash >> sp->hash_shift) ^ (hash & sp->hash_mask);
|
85
|
+
}
|
86
|
+
|
87
|
+
static inline void shared_pool_lock_item(struct item *item) {
|
88
|
+
while (__sync_lock_test_and_set(&item->lock, 1)) {
|
89
|
+
while(item->lock)
|
90
|
+
; // spin with less stress
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
static inline void shared_pool_unlock_item(struct item *item) {
|
95
|
+
__sync_lock_release(&item->lock);
|
96
|
+
}
|
97
|
+
|
98
|
+
static inline struct item *t_find_and_lock(struct shared_pool *sp, char *key, size_t klen) {
|
99
|
+
uint32_t index = FNV32(sp,key,klen);
|
100
|
+
struct item *item = ITEM(sp,index);
|
101
|
+
shared_pool_lock_item(item);
|
102
|
+
if ((item->expire_at == 0 || item->expire_at > time(NULL)) && klen > 0 && item->key_len == klen && memcmp(ITEM_KEY(item),key,klen) == 0)
|
103
|
+
return item;
|
104
|
+
shared_pool_unlock_item(item);
|
105
|
+
return NULL;
|
106
|
+
}
|
107
|
+
|
108
|
+
static inline struct item *t_add_and_lock(struct shared_pool *sp,char *key, size_t klen, uint8_t *p, size_t len,uint32_t expire_after) {
|
109
|
+
if (len <= 0 || len + sizeof(struct item) >= sp->item_size)
|
110
|
+
return NULL;
|
111
|
+
uint32_t index = FNV32(sp,key,klen);
|
112
|
+
struct item *item = ITEM(sp,index);
|
113
|
+
shared_pool_lock_item(item);
|
114
|
+
item->key_len = klen;
|
115
|
+
item->blob_len = len;
|
116
|
+
if (expire_after > 0)
|
117
|
+
item->expire_at = time(NULL) + expire_after;
|
118
|
+
else
|
119
|
+
item->expire_at = 0;
|
120
|
+
|
121
|
+
memcpy(ITEM_KEY(item),key,klen);
|
122
|
+
memcpy(ITEM_BLOB(item),p,len);
|
123
|
+
return item;
|
124
|
+
}
|
125
|
+
|
126
|
+
static inline int t_add(struct shared_pool *sp,char *key, size_t klen, uint8_t *p, size_t len,uint32_t expire_after) {
|
127
|
+
struct item *item = t_add_and_lock(sp,key,klen,p,len,expire_after);
|
128
|
+
if (item != NULL) {
|
129
|
+
shared_pool_unlock_item(item);
|
130
|
+
return 0;
|
131
|
+
} else {
|
132
|
+
return -EFAULT;
|
133
|
+
}
|
134
|
+
}
|
135
|
+
static inline int t_add_and_send_to(struct shared_pool *sp,char *key, size_t klen, uint8_t *p, size_t len,uint32_t expire_after,struct in_addr ip, uint16_t port) {
|
136
|
+
if (sp->c_sock == 0) {
|
137
|
+
if ((sp->c_sock = socket(AF_INET,SOCK_DGRAM,0)) < 0)
|
138
|
+
SAYPX("socket");
|
139
|
+
int op = 1;
|
140
|
+
setsockopt(sp->c_sock, SOL_SOCKET, SO_BROADCAST, (char *) &op, sizeof(op));
|
141
|
+
}
|
142
|
+
|
143
|
+
struct sockaddr_in addr;
|
144
|
+
memset(&addr,0,sizeof(addr));
|
145
|
+
addr.sin_family = AF_INET;
|
146
|
+
addr.sin_addr = ip;
|
147
|
+
addr.sin_port = htons(port);
|
148
|
+
struct item *item = t_add_and_lock(sp,key,klen,p,len,expire_after);
|
149
|
+
if (item == NULL)
|
150
|
+
return -EFAULT;
|
151
|
+
|
152
|
+
int rc = sendto(sp->c_sock,item,sp->item_size,0,(struct sockaddr *)&addr,sizeof(addr));
|
153
|
+
shared_pool_unlock_item(item);
|
154
|
+
if (rc != sp->item_size)
|
155
|
+
return -EFAULT;
|
156
|
+
return 0;
|
157
|
+
}
|
158
|
+
|
159
|
+
static void t_bind_and_wait_for_updates(struct shared_pool *sp, struct in_addr ip, uint16_t port) {
|
160
|
+
if ((sp->s_sock = socket(AF_INET,SOCK_DGRAM,0)) < 0)
|
161
|
+
SAYPX("socket");
|
162
|
+
|
163
|
+
struct sockaddr_in addr;
|
164
|
+
memset(&addr,0,sizeof(addr));
|
165
|
+
addr.sin_family = AF_INET;
|
166
|
+
addr.sin_addr = ip;
|
167
|
+
addr.sin_port = htons(port);
|
168
|
+
|
169
|
+
if (bind(sp->s_sock,(struct sockaddr *)&addr,sizeof(addr)) < 0)
|
170
|
+
SAYPX("bind failed on socket: %d on ip: %d",sp->s_sock,ip.s_addr);
|
171
|
+
|
172
|
+
int rc;
|
173
|
+
uint8_t buf[sp->item_size];
|
174
|
+
for (;;) {
|
175
|
+
rc = recv(sp->s_sock,buf,sp->item_size,MSG_WAITALL);
|
176
|
+
if (rc == sp->item_size) {
|
177
|
+
struct item *value = (struct item *) buf;
|
178
|
+
uint32_t expire_after = value->expire_at > 0 ? value->expire_at - time(NULL) : 0;
|
179
|
+
if (t_add(sp,(char *)ITEM_KEY(value),value->key_len,ITEM_BLOB(value),value->blob_len,expire_after) < 0)
|
180
|
+
E("unable to store input key (klen: %d, blen: %d)",value->key_len,value->blob_len);
|
181
|
+
else
|
182
|
+
D("added remote key");
|
183
|
+
} else {
|
184
|
+
if (rc > 0) {
|
185
|
+
E("received bad frame expected item size: %d got %d",sp->item_size,rc);
|
186
|
+
} else {
|
187
|
+
BAD_DIE("recv failed");
|
188
|
+
break;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
static struct shared_pool *shared_pool_init(int key, int hash_bits, int item_size) {
|
195
|
+
if (hash_bits < 16 || hash_bits > 32 )
|
196
|
+
SAYX("hash bits must be between 16 and 32");
|
197
|
+
|
198
|
+
if (item_size <= sizeof(struct item))
|
199
|
+
SAYX("item size must be at least %zu",sizeof(struct item));
|
200
|
+
|
201
|
+
struct shared_pool *sp;
|
202
|
+
BAD_ALLOC(sp,struct shared_pool);
|
203
|
+
sp->c_sock = 0;
|
204
|
+
sp->s_sock = 0;
|
205
|
+
sp->pool = NULL;
|
206
|
+
sp->item_size = item_size;
|
207
|
+
sp->hash_shift = hash_bits;
|
208
|
+
sp->hash_mask = (((uint32_t) 1 << hash_bits) - 1);
|
209
|
+
sp->key = key;
|
210
|
+
|
211
|
+
int flags = 0666 | IPC_CREAT;
|
212
|
+
#define SHMGET(sp) shmget(sp->key, sp->item_size * (sp->hash_mask + 1), flags)
|
213
|
+
if ((sp->shm_id = SHMGET(sp)) < 0)
|
214
|
+
SAYPX("shmget: 0x%x",key);
|
215
|
+
#undef SHMGET
|
216
|
+
|
217
|
+
if ((sp->pool = shmat(sp->shm_id, NULL, 0)) < 0 )
|
218
|
+
SAYPX("shmat");
|
219
|
+
|
220
|
+
return sp;
|
221
|
+
}
|
222
|
+
|
223
|
+
static void shared_pool_reset(struct shared_pool *sp) {
|
224
|
+
int i;
|
225
|
+
for (i = 0; i < sp->hash_mask + 1; i++) {
|
226
|
+
struct item *item = ITEM(sp,i);
|
227
|
+
item->expire_at = 1;
|
228
|
+
}
|
229
|
+
}
|
230
|
+
|
231
|
+
// this is really bad - please dont use it unless you want to debug something
|
232
|
+
static void shared_pool_blind_guardian(struct shared_pool *sp, int interval_us,int cycles,int warn_only) {
|
233
|
+
int i,attempts;
|
234
|
+
struct item *item;
|
235
|
+
for (;;) {
|
236
|
+
for (i = 0; i < sp->hash_mask + 1; i++) {
|
237
|
+
item = ITEM(sp,i);
|
238
|
+
if (item->key_len > 0) {
|
239
|
+
attempts = 0;
|
240
|
+
while (item->lock == 1) { // no barrier
|
241
|
+
if (attempts++ > cycles) {
|
242
|
+
char buf[BUFSIZ];
|
243
|
+
int len = item->key_len > BUFSIZ - 1 ? BUFSIZ - 1 : item->key_len;
|
244
|
+
memcpy(buf,ITEM_KEY(item),len);
|
245
|
+
buf[len] = '\0';
|
246
|
+
E("unlocking - %s, locked for more then %d cycles",buf,cycles);
|
247
|
+
if (!warn_only) {
|
248
|
+
item->lock = 0;
|
249
|
+
item->expire_at = 1;
|
250
|
+
}
|
251
|
+
}
|
252
|
+
}
|
253
|
+
}
|
254
|
+
}
|
255
|
+
usleep(interval_us);
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
static void shared_pool_destroy(struct shared_pool *sp) {
|
260
|
+
if (shmdt(sp->pool) != 0)
|
261
|
+
SAYPX("detach failed");
|
262
|
+
|
263
|
+
struct shmid_ds ds;
|
264
|
+
|
265
|
+
if (shmctl(sp->shm_id, IPC_STAT, &ds) != 0)
|
266
|
+
SAYPX("IPC_STAT failed");
|
267
|
+
if (ds.shm_nattch == 0) {
|
268
|
+
if (shmctl(sp->shm_id, IPC_RMID, NULL) != 0)
|
269
|
+
SAYPX("IPC_RMID failed on shm_id %d",sp->shm_id);
|
270
|
+
}
|
271
|
+
BAD_FREE(sp);
|
272
|
+
}
|
273
|
+
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xbad
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Borislav Nikolov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-11-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake-compiler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: xbad - shared memory key/value cache..
|
28
|
+
email: jack@sofialondonmoskva.com
|
29
|
+
executables: []
|
30
|
+
extensions:
|
31
|
+
- ext/xbad/extconf.rb
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ext/xbad/xbad.c
|
35
|
+
- ext/xbad/xbad.h
|
36
|
+
- ext/xbad/extconf.rb
|
37
|
+
homepage: https://github.com/jackdoe/0XBAD
|
38
|
+
licenses: []
|
39
|
+
metadata: {}
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 2.0.3
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: xbad - shared memory key/value store
|
60
|
+
test_files: []
|