xbad 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|