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 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: []