xbad 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,2 @@
1
+ require "mkmf"
2
+ create_makefile("xbad")
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: []