localmemcache 0.0.1 → 0.2.0
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.
- data/.gitignore +17 -0
- data/INTERNALS +26 -0
- data/README +18 -58
- data/Rakefile +11 -4
- data/THANKS +1 -0
- data/VERSION +1 -1
- data/configure +0 -0
- data/example/compile.sh +3 -0
- data/example/hello +10 -0
- data/example/hello.bin +0 -0
- data/example/hello.c +28 -0
- data/example/hello.rb +8 -0
- data/src/Makefile.in +5 -4
- data/src/lmc_common.c +43 -0
- data/src/lmc_common.h +26 -0
- data/src/lmc_error.c +9 -3
- data/src/lmc_error.h +12 -2
- data/src/lmc_hashtable.c +163 -35
- data/src/lmc_hashtable.h +14 -6
- data/src/lmc_lock.c +69 -11
- data/src/lmc_lock.h +3 -0
- data/src/lmc_shm.c +25 -8
- data/src/lmc_shm.h +1 -0
- data/src/lmc_valloc.c +211 -59
- data/src/lmc_valloc.h +23 -3
- data/src/localmemcache.c +193 -31
- data/src/localmemcache.h +17 -5
- data/src/ruby-binding/localmemcache.rb +13 -3
- data/src/ruby-binding/rblocalmemcache.c +107 -17
- data/src/tests/alloc +0 -0
- data/src/tests/allocfailure +15 -0
- data/src/tests/allocfailure.rb +20 -0
- data/src/tests/bench +0 -0
- data/src/tests/bench.rb +15 -0
- data/src/tests/bench_keys +11 -0
- data/src/tests/bench_keys.rb +24 -0
- data/src/tests/crash +19 -0
- data/src/tests/crash.rb +41 -0
- data/src/tests/lmc +0 -0
- data/src/tests/lmc.rb +22 -47
- data/src/tests/parallelwrite +15 -0
- data/src/tests/parallelwrite.rb +27 -0
- data/src/tests/runtest.sh +0 -0
- data/src/tests/shm +0 -0
- data/src/tests/ttalloc +0 -0
- data/src/tests/ttlmc +0 -0
- data/src/tests/ttlmc.rb +18 -2
- metadata +22 -4
data/src/lmc_valloc.h
CHANGED
@@ -11,7 +11,13 @@ typedef struct {
|
|
11
11
|
size_t total_free_mem;
|
12
12
|
size_t free_mem;
|
13
13
|
size_t largest_chunk;
|
14
|
-
}
|
14
|
+
} lmc_mem_status_t;
|
15
|
+
|
16
|
+
typedef struct {
|
17
|
+
int op_id;
|
18
|
+
size_t p1;
|
19
|
+
size_t p2;
|
20
|
+
} lmc_log_descriptor_t;
|
15
21
|
|
16
22
|
typedef struct {
|
17
23
|
size_t first_free;
|
@@ -20,12 +26,26 @@ typedef struct {
|
|
20
26
|
size_t magic;
|
21
27
|
size_t va_hash;
|
22
28
|
int locked;
|
23
|
-
|
29
|
+
lmc_log_descriptor_t log;
|
30
|
+
} lmc_mem_descriptor_t;
|
31
|
+
|
32
|
+
typedef struct {
|
33
|
+
size_t next;
|
34
|
+
size_t size;
|
35
|
+
} lmc_mem_chunk_descriptor_t;
|
24
36
|
|
25
37
|
|
26
38
|
size_t lmc_valloc(void *base, size_t size);
|
27
39
|
void lmc_free(void *base, size_t chunk);
|
28
|
-
|
40
|
+
lmc_mem_status_t lmc_status(void *base, char *where);
|
29
41
|
int is_lmc_already_initialized(void *base);
|
30
42
|
void lmc_init_memory(void *ptr, size_t size);
|
43
|
+
|
44
|
+
int lmc_um_mark_allocated(void *base, char *bf, size_t va);
|
45
|
+
char *lmc_um_new_mem_usage_bitmap(void *base);
|
46
|
+
int lmc_um_find_leaks(void *base, char *bf);
|
47
|
+
|
48
|
+
lmc_log_descriptor_t *lmc_log_op(void *base, int opid);
|
49
|
+
void lmc_log_finish(void *base);
|
50
|
+
|
31
51
|
#endif
|
data/src/localmemcache.c
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2009, Sven C. Koehler
|
3
|
+
*/
|
4
|
+
|
1
5
|
#include "localmemcache.h"
|
2
6
|
#include <stdio.h>
|
3
7
|
#include <string.h>
|
@@ -5,10 +9,10 @@
|
|
5
9
|
#include "lmc_shm.h"
|
6
10
|
|
7
11
|
int lmc_set_lock_flag(void *base, lmc_error_t *e) {
|
8
|
-
|
12
|
+
lmc_mem_descriptor_t *md = base;
|
9
13
|
if (md->locked != 0) {
|
10
|
-
|
11
|
-
"may be corrupt
|
14
|
+
lmc_handle_error_with_err_string("lmc_set_lock_flag",
|
15
|
+
"Failed to lock shared memory--may be corrupt!", "ShmLockFailed", e);
|
12
16
|
return 0;
|
13
17
|
} else {
|
14
18
|
md->locked = 1;
|
@@ -17,10 +21,11 @@ int lmc_set_lock_flag(void *base, lmc_error_t *e) {
|
|
17
21
|
}
|
18
22
|
|
19
23
|
int lmc_release_lock_flag(void *base, lmc_error_t *e) {
|
20
|
-
|
24
|
+
lmc_mem_descriptor_t *md = base;
|
21
25
|
if (md->locked != 1) {
|
22
|
-
|
23
|
-
"--may be corrupt
|
26
|
+
lmc_handle_error_with_err_string("lmc_release_lock_flag",
|
27
|
+
"Shared memory appears to be unlocked already--may be corrupt!",
|
28
|
+
"ShmUnlockFailed", e);
|
24
29
|
return 0;
|
25
30
|
} else {
|
26
31
|
md->locked = 0;
|
@@ -28,46 +33,93 @@ int lmc_release_lock_flag(void *base, lmc_error_t *e) {
|
|
28
33
|
return 1;
|
29
34
|
}
|
30
35
|
|
36
|
+
void lmc_clean_namespace_string(char *result, const char* original) {
|
37
|
+
size_t n = strlen(original);
|
38
|
+
if (n > 256) { n = 256; }
|
39
|
+
const char *s = original;
|
40
|
+
char *d = result;
|
41
|
+
char ch;
|
42
|
+
for (; n--; d++, s++) {
|
43
|
+
ch = *s;
|
44
|
+
if ((ch >= 'a' && ch <= 'z') ||
|
45
|
+
(ch >= 'A' && ch <= 'Z')) {
|
46
|
+
*d = ch;
|
47
|
+
} else {
|
48
|
+
*d = '-';
|
49
|
+
}
|
50
|
+
}
|
51
|
+
*d = 0x0;
|
52
|
+
}
|
53
|
+
|
31
54
|
int local_memcache_clear_namespace(const char *namespace, int repair,
|
32
55
|
lmc_error_t *e) {
|
33
|
-
|
56
|
+
char clean_ns[1024];
|
57
|
+
lmc_clean_namespace_string((char *)clean_ns, namespace);
|
58
|
+
lmc_clean_namespace((char *)clean_ns, e);
|
34
59
|
if (repair) {
|
35
|
-
lmc_lock_t *l = lmc_lock_init(
|
60
|
+
lmc_lock_t *l = lmc_lock_init((char *)clean_ns, 1, e);
|
36
61
|
lmc_lock_repair(l);
|
37
62
|
free(l);
|
63
|
+
char check_lock_name[1024];
|
64
|
+
snprintf((char *)&check_lock_name, 1023, "%s-check", (char *)clean_ns);
|
65
|
+
lmc_lock_t *check_l;
|
66
|
+
check_l = lmc_lock_init(check_lock_name, 1, e);
|
67
|
+
lmc_lock_repair(check_l);
|
68
|
+
free(check_l);
|
38
69
|
}
|
39
70
|
return 1;
|
40
71
|
}
|
41
72
|
|
42
|
-
local_memcache_t *
|
43
|
-
lmc_error_t* e) {
|
73
|
+
local_memcache_t *__local_memcache_create(const char *namespace, size_t size,
|
74
|
+
int force, int *ok, lmc_error_t* e) {
|
75
|
+
int d;
|
76
|
+
if (!ok) { ok = &d; }
|
77
|
+
*ok = 1;
|
44
78
|
local_memcache_t *lmc = calloc(1, sizeof(local_memcache_t));
|
45
79
|
if (!lmc || (lmc->namespace = strdup(namespace)) == NULL) return NULL;
|
46
80
|
lmc->size = size;
|
47
81
|
if ((lmc->lock = lmc_lock_init(lmc->namespace, 1, e)) == NULL) goto failed;
|
48
|
-
|
49
|
-
|
82
|
+
int retry_counter = 0;
|
83
|
+
retry:
|
84
|
+
if (retry_counter++ > 10) {
|
85
|
+
lmc_handle_error_with_err_string("local_memcache_create",
|
86
|
+
"Too many retries: Failed to repair shared memory!",
|
87
|
+
"ShmLockFailed", e);
|
50
88
|
goto failed;
|
51
89
|
}
|
90
|
+
if (!lmc_is_lock_working(lmc->lock, e)) {
|
91
|
+
if (!force) {
|
92
|
+
if (local_memcache_check_namespace(namespace, e)) goto retry;
|
93
|
+
lmc_handle_error_with_err_string("local_memcache_create",
|
94
|
+
"Failed to repair shared memory!", "ShmLockFailed", e);
|
95
|
+
goto failed;
|
96
|
+
}
|
97
|
+
*ok = 0;
|
98
|
+
}
|
52
99
|
{
|
53
|
-
if (!lmc_lock_obtain("local_memcache_create", lmc->lock, &lmc->error))
|
100
|
+
if (*ok && !lmc_lock_obtain("local_memcache_create", lmc->lock, &lmc->error))
|
54
101
|
goto failed;
|
55
102
|
if ((lmc->shm = lmc_shm_create(lmc->namespace, lmc->size, 0, e)) == NULL)
|
56
103
|
goto release_and_fail;
|
57
104
|
lmc->base = lmc->shm->base;
|
58
|
-
if (is_lmc_already_initialized(lmc->base)) {
|
59
|
-
if (!lmc_set_lock_flag(lmc->base, e))
|
60
|
-
|
105
|
+
if (!*ok || is_lmc_already_initialized(lmc->base)) {
|
106
|
+
if (*ok && !lmc_set_lock_flag(lmc->base, e)) {
|
107
|
+
if (!force) goto release_and_fail;
|
108
|
+
*ok = 0;
|
109
|
+
}
|
110
|
+
lmc_mem_descriptor_t *md = lmc->base;
|
61
111
|
lmc->va_hash = md->va_hash;
|
62
112
|
} else {
|
63
113
|
lmc_init_memory(lmc->base, lmc->size);
|
64
|
-
|
114
|
+
lmc_mem_descriptor_t *md = lmc->base;
|
65
115
|
if ((md->va_hash = ht_hash_create(lmc->base, e)) == 0)
|
66
116
|
goto unlock_and_fail;
|
67
117
|
lmc->va_hash = md->va_hash;
|
68
118
|
}
|
69
|
-
|
70
|
-
|
119
|
+
if (*ok) {
|
120
|
+
lmc_release_lock_flag(lmc->base, e);
|
121
|
+
lmc_lock_release("local_memcache_create", lmc->lock, e);
|
122
|
+
}
|
71
123
|
}
|
72
124
|
return lmc;
|
73
125
|
|
@@ -76,12 +128,106 @@ unlock_and_fail:
|
|
76
128
|
release_and_fail:
|
77
129
|
lmc_lock_release("local_memcache_create", lmc->lock, e);
|
78
130
|
failed:
|
131
|
+
*ok = 0;
|
79
132
|
free(lmc);
|
80
133
|
return NULL;
|
81
134
|
}
|
82
135
|
|
136
|
+
local_memcache_t *local_memcache_create(const char *namespace, double size_mb,
|
137
|
+
lmc_error_t* e) {
|
138
|
+
char clean_ns[1024];
|
139
|
+
double s = size_mb == 0.0 ? 1024.0 : size_mb;
|
140
|
+
size_t si = s * 1024 * 1024;
|
141
|
+
//printf("size: %f, s: %f, si: %zd\n", size_mb, s, si);
|
142
|
+
lmc_clean_namespace_string((char *)clean_ns, namespace);
|
143
|
+
return __local_memcache_create((char *)clean_ns, si, 0, 0, e);
|
144
|
+
}
|
145
|
+
|
146
|
+
int local_memcache_check_namespace(const char *namespace, lmc_error_t *e) {
|
147
|
+
char clean_ns[1024];
|
148
|
+
lmc_clean_namespace_string((char *)clean_ns, namespace);
|
149
|
+
char check_lock_name[1024];
|
150
|
+
snprintf((char *)check_lock_name, 1023, "%s-check", (char *)clean_ns);
|
151
|
+
|
152
|
+
if (!lmc_does_namespace_exist((char *)clean_ns)) {
|
153
|
+
lmc_clear_namespace_lock(check_lock_name);
|
154
|
+
lmc_clear_namespace_lock(namespace);
|
155
|
+
printf("namespace '%s' does not exist!\n", (char *)clean_ns);
|
156
|
+
return 1;
|
157
|
+
}
|
158
|
+
|
159
|
+
lmc_lock_t *check_l;
|
160
|
+
if ((check_l = lmc_lock_init(check_lock_name, 1, e)) == NULL) {
|
161
|
+
lmc_handle_error_with_err_string("lmc_lock_init",
|
162
|
+
"Unable to initialize lock for checking namespace", "LockError", e);
|
163
|
+
return 0;
|
164
|
+
}
|
165
|
+
if (!lmc_lock_obtain_mandatory("local_memcache_check_namespace",
|
166
|
+
check_l, e)) goto check_lock_failed;
|
167
|
+
lmc_mem_descriptor_t *md = 0;
|
168
|
+
size_t ns_size = lmc_namespace_size((char *)clean_ns);
|
169
|
+
int ok;
|
170
|
+
local_memcache_t *lmc = __local_memcache_create((char *)clean_ns, ns_size,
|
171
|
+
1, &ok, e);
|
172
|
+
if (!lmc) {
|
173
|
+
printf("WOAH: lmc == 0!\n");
|
174
|
+
goto failed;
|
175
|
+
}
|
176
|
+
md = lmc->base;
|
177
|
+
if (!ok) {
|
178
|
+
printf("[lmc] Auto repairing namespace '%s'\n", namespace);
|
179
|
+
if (!md->locked) goto release;
|
180
|
+
if (md->log.op_id == 0) goto unlock_and_release;
|
181
|
+
if (ht_redo(lmc->base, md->va_hash, &md->log, e)) goto unlock_and_release;
|
182
|
+
goto failed;
|
183
|
+
}
|
184
|
+
goto release_but_no_lock_correction;
|
185
|
+
|
186
|
+
unlock_and_release:
|
187
|
+
if (md) {
|
188
|
+
if (!ht_check_memory(lmc->base, md->va_hash)) goto failed;
|
189
|
+
md->locked = 0;
|
190
|
+
}
|
191
|
+
release:
|
192
|
+
{
|
193
|
+
int v;
|
194
|
+
sem_getvalue(lmc->lock->sem, &v);
|
195
|
+
if (v == 0) {
|
196
|
+
lmc_lock_release("local_memcache_create", lmc->lock, e);
|
197
|
+
}
|
198
|
+
}
|
199
|
+
release_but_no_lock_correction:
|
200
|
+
local_memcache_free(lmc, e);
|
201
|
+
lmc_lock_release("local_memcache_check_namespace", check_l, e);
|
202
|
+
free(check_l);
|
203
|
+
return 1;
|
204
|
+
failed:
|
205
|
+
lmc_handle_error_with_err_string("local_memcache_check_namespace",
|
206
|
+
"Unable to recover namespace", "RecoveryFailed", e);
|
207
|
+
local_memcache_free(lmc, e);
|
208
|
+
lmc_lock_release("local_memcache_check_namespace", check_l, e);
|
209
|
+
check_lock_failed:
|
210
|
+
free(check_l);
|
211
|
+
printf("[lmc] Failed to repair namespace '%s'\n", namespace);
|
212
|
+
return 0;
|
213
|
+
}
|
214
|
+
|
215
|
+
|
83
216
|
int lmc_lock_shm_region(const char *who, local_memcache_t *lmc) {
|
84
|
-
|
217
|
+
int r;
|
218
|
+
int retry_counter = 0;
|
219
|
+
retry:
|
220
|
+
if (retry_counter++ > 10) {
|
221
|
+
printf("[lmc] Too many retries: Cannot repair namespace '%s'\n",
|
222
|
+
lmc->namespace);
|
223
|
+
return 0;
|
224
|
+
}
|
225
|
+
r = lmc_lock_obtain(who, lmc->lock, &lmc->error);
|
226
|
+
if (!r && (strcmp(lmc->error.error_type, "LockTimedOut") == 0)) {
|
227
|
+
if (local_memcache_check_namespace(lmc->namespace, &lmc->error)) goto retry;
|
228
|
+
printf("[lmc] Cannot repair namespace '%s'\n", lmc->namespace);
|
229
|
+
}
|
230
|
+
if (!r) return 0;
|
85
231
|
if (!lmc_set_lock_flag(lmc->base, &lmc->error)) {
|
86
232
|
lmc_lock_release(who, lmc->lock, &lmc->error);
|
87
233
|
return 0;
|
@@ -96,35 +242,51 @@ int lmc_unlock_shm_region(const char *who, local_memcache_t *lmc) {
|
|
96
242
|
return r;
|
97
243
|
}
|
98
244
|
|
99
|
-
char *
|
245
|
+
const char *__local_memcache_get(local_memcache_t *lmc,
|
246
|
+
const char *key, size_t n_key, size_t *n_value) {
|
100
247
|
if (!lmc_lock_shm_region("local_memcache_get", lmc)) return 0;
|
101
|
-
char *r = ht_get(lmc->base, lmc->va_hash, key);
|
102
|
-
if (!lmc_unlock_shm_region("local_memcache_get", lmc)) return 0;
|
248
|
+
const char *r = ht_get(lmc->base, lmc->va_hash, key, n_key, n_value);
|
103
249
|
return r;
|
104
250
|
}
|
105
251
|
|
252
|
+
char *local_memcache_get_new(local_memcache_t *lmc,
|
253
|
+
const char *key, size_t n_key, size_t *n_value) {
|
254
|
+
const char *r = __local_memcache_get(lmc, key, n_key, n_value);
|
255
|
+
char *new_s = malloc(*n_value);
|
256
|
+
memcpy(new_s, r, *n_value);
|
257
|
+
if (!lmc_unlock_shm_region("local_memcache_get_new", lmc)) return 0;
|
258
|
+
return new_s;
|
259
|
+
}
|
260
|
+
|
106
261
|
int local_memcache_set(local_memcache_t *lmc,
|
107
|
-
const char *key, const char* value) {
|
262
|
+
const char *key, size_t n_key, const char* value, size_t n_value) {
|
108
263
|
if (!lmc_lock_shm_region("local_memcache_set", lmc)) return 0;
|
109
|
-
int r = ht_set(lmc->base, lmc->va_hash, key, value,
|
110
|
-
|
264
|
+
int r = ht_set(lmc->base, lmc->va_hash, key, n_key, value, n_value,
|
265
|
+
&lmc->error);
|
266
|
+
if (!lmc_unlock_shm_region("local_memcache_set", lmc)) return 0;
|
111
267
|
return r;
|
112
268
|
}
|
113
269
|
|
114
|
-
int local_memcache_delete(local_memcache_t *lmc, char *key) {
|
270
|
+
int local_memcache_delete(local_memcache_t *lmc, char *key, size_t n_key) {
|
115
271
|
if (!lmc_lock_shm_region("local_memcache_delete", lmc)) return 0;
|
116
|
-
int r = ht_delete(lmc->base, lmc->va_hash, key);
|
272
|
+
int r = ht_delete(lmc->base, lmc->va_hash, key, n_key);
|
117
273
|
if (!lmc_unlock_shm_region("local_memcache_delete", lmc)) return 0;
|
118
274
|
return r;
|
119
275
|
}
|
120
276
|
|
121
|
-
int local_memcache_free(local_memcache_t *lmc) {
|
122
|
-
lmc_error_t e;
|
277
|
+
int local_memcache_free(local_memcache_t *lmc, lmc_error_t *e) {
|
123
278
|
if (!lmc_lock_shm_region("local_memcache_free", lmc)) return 0;
|
124
279
|
int r = ht_hash_destroy(lmc->base, lmc->va_hash);
|
125
280
|
if (!lmc_unlock_shm_region("local_memcache_free", lmc)) return 0;
|
126
|
-
lmc_shm_destroy(lmc->shm,
|
281
|
+
lmc_shm_destroy(lmc->shm, e);
|
127
282
|
free(lmc->namespace);
|
128
283
|
free(lmc->lock);
|
129
284
|
return r;
|
130
285
|
}
|
286
|
+
|
287
|
+
int local_memcache_iterate(local_memcache_t *lmc, void *ctx, ITERATOR_P(iter)) {
|
288
|
+
if (!lmc_lock_shm_region("local_memcache_iterate", lmc)) return 0;
|
289
|
+
int r = ht_hash_iterate(lmc->base, lmc->va_hash, ctx, iter);
|
290
|
+
if (!lmc_unlock_shm_region("local_memcache_iterate", lmc)) return 0;
|
291
|
+
return r;
|
292
|
+
}
|
data/src/localmemcache.h
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2009, Sven C. Koehler
|
3
|
+
*/
|
4
|
+
|
1
5
|
#ifndef _LOCAL_MEMCACHE_INCLUDED_
|
2
6
|
#define _LOCAL_MEMCACHE_INCLUDED_
|
3
7
|
|
@@ -6,6 +10,7 @@
|
|
6
10
|
#include "lmc_shm.h"
|
7
11
|
#include "lmc_lock.h"
|
8
12
|
#include "lmc_error.h"
|
13
|
+
#include "lmc_common.h"
|
9
14
|
|
10
15
|
#define LOCAL_MEMCACHE_FAILED 0
|
11
16
|
#define LOCAL_MEMCACHE_SUCCESS 1
|
@@ -21,13 +26,20 @@ typedef struct {
|
|
21
26
|
lmc_error_t error;
|
22
27
|
} local_memcache_t;
|
23
28
|
|
24
|
-
local_memcache_t *local_memcache_create(const char *namespace,
|
29
|
+
local_memcache_t *local_memcache_create(const char *namespace, double size_mb,
|
25
30
|
lmc_error_t *e);
|
26
|
-
char *
|
27
|
-
|
28
|
-
int
|
29
|
-
|
31
|
+
char *local_memcache_get_new(local_memcache_t *lmc, const char *key,
|
32
|
+
size_t n_key, size_t *n_value);
|
33
|
+
int local_memcache_set(local_memcache_t *lmc, const char *key, size_t n_key,
|
34
|
+
const char* value, size_t n_value);
|
35
|
+
int local_memcache_delete(local_memcache_t *lmc, char *key, size_t n_key);
|
36
|
+
int local_memcache_free(local_memcache_t *lmc, lmc_error_t *e);
|
37
|
+
int local_memcache_iterate(local_memcache_t *lmc, void *ctx, ITERATOR_P(iter));
|
30
38
|
int local_memcache_clear_namespace(const char *namespace, int repair,
|
31
39
|
lmc_error_t *e);
|
40
|
+
int local_memcache_check_namespace(const char *namespace, lmc_error_t *e);
|
41
|
+
|
42
|
+
const char *__local_memcache_get(local_memcache_t *lmc,
|
43
|
+
const char *key, size_t n_key, size_t *n_value);
|
32
44
|
|
33
45
|
#endif
|
@@ -9,6 +9,16 @@ require 'rblocalmemcache'
|
|
9
9
|
#
|
10
10
|
class LocalMemCache
|
11
11
|
|
12
|
+
class LocalMemCacheError < StandardError; end
|
13
|
+
class ShmError < LocalMemCacheError; end
|
14
|
+
class MemoryPoolFull < LocalMemCacheError; end
|
15
|
+
class LockError < LocalMemCacheError; end
|
16
|
+
class LockTimedOut < LocalMemCacheError; end
|
17
|
+
class OutOfMemoryError < LocalMemCacheError; end
|
18
|
+
class RecoveryFailed < LocalMemCacheError; end
|
19
|
+
class ShmLockFailed < LocalMemCacheError; end
|
20
|
+
class ShmUnlockFailed < LocalMemCacheError; end
|
21
|
+
|
12
22
|
# Creates a new handle for accessing a shared memory region.
|
13
23
|
#
|
14
24
|
# LocalMemCache.new :namespace=>"foo", :size_mb=> 1
|
@@ -17,9 +27,9 @@ class LocalMemCache
|
|
17
27
|
# The size_mb defaults to 1024 (1 GB).
|
18
28
|
#
|
19
29
|
def self.new(options)
|
20
|
-
o = { :size_mb =>
|
30
|
+
o = { :size_mb => 0 }.update(options || {})
|
21
31
|
raise "Missing mandatory option ':namespace'" if !o[:namespace]
|
22
|
-
_new(o[:namespace].
|
32
|
+
_new(o[:namespace].to_s, o[:size_mb].to_f);
|
23
33
|
end
|
24
34
|
|
25
35
|
# Deletes the given namespaces, removing semaphores if necessary.
|
@@ -27,6 +37,6 @@ class LocalMemCache
|
|
27
37
|
# processes.
|
28
38
|
#
|
29
39
|
def self.clear_namespace(namespace, repair = false)
|
30
|
-
_clear_namespace(namespace, repair)
|
40
|
+
_clear_namespace(namespace.to_s, repair)
|
31
41
|
end
|
32
42
|
end
|
@@ -8,42 +8,83 @@
|
|
8
8
|
/* :nodoc: */
|
9
9
|
long long_value(VALUE i) { return NUM2LONG(rb_Integer(i)); }
|
10
10
|
/* :nodoc: */
|
11
|
+
double double_value(VALUE i) { return NUM2DBL(i); }
|
12
|
+
/* :nodoc: */
|
11
13
|
VALUE num2string(long i) { return rb_big2str(rb_int2big(i), 10); }
|
12
14
|
/* :nodoc: */
|
13
15
|
char *rstring_ptr(VALUE s) {
|
14
16
|
char* r = NIL_P(s) ? "nil" : RSTRING_PTR(rb_String(s));
|
15
17
|
return r ? r : "nil";
|
16
18
|
}
|
19
|
+
|
20
|
+
size_t rstring_length(VALUE s) {
|
21
|
+
size_t r = NIL_P(s) ? 0 : RSTRING_LEN(rb_String(s));
|
22
|
+
return r;
|
23
|
+
}
|
17
24
|
/* :nodoc: */
|
18
|
-
static VALUE ruby_string(char *s) { return s ? rb_str_new2(s) : Qnil; }
|
25
|
+
static VALUE ruby_string(const char *s) { return s ? rb_str_new2(s) : Qnil; }
|
19
26
|
/* :nodoc: */
|
20
27
|
int bool_value(VALUE v) { return v == Qtrue; }
|
21
28
|
|
22
|
-
static VALUE
|
29
|
+
static VALUE lmc_ruby_string2(const char *s, size_t l) {
|
30
|
+
return s ? rb_str_new(s, l) : Qnil;
|
31
|
+
}
|
32
|
+
|
33
|
+
static VALUE lmc_ruby_string(const char *s) {
|
34
|
+
return lmc_ruby_string2(s + sizeof(size_t), *(size_t *) s);
|
35
|
+
}
|
36
|
+
|
37
|
+
|
38
|
+
static VALUE LocalMemCache;
|
23
39
|
|
24
40
|
/* :nodoc: */
|
25
|
-
void raise_exception(
|
26
|
-
|
41
|
+
void raise_exception(lmc_error_t *e) {
|
42
|
+
VALUE eid = rb_intern(e->error_type);
|
43
|
+
VALUE k = rb_const_get(LocalMemCache, eid);
|
44
|
+
rb_raise(k, e->error_str);
|
27
45
|
}
|
28
46
|
|
29
47
|
/* :nodoc: */
|
30
|
-
static VALUE LocalMemCache__new2(VALUE klass, VALUE namespace, VALUE
|
48
|
+
static VALUE LocalMemCache__new2(VALUE klass, VALUE namespace, VALUE size_mb) {
|
31
49
|
lmc_error_t e;
|
32
50
|
local_memcache_t *lmc = local_memcache_create(rstring_ptr(namespace),
|
33
|
-
|
34
|
-
if (!lmc) { raise_exception(
|
51
|
+
double_value(size_mb), &e);
|
52
|
+
if (!lmc) { raise_exception(&e); }
|
35
53
|
return Data_Wrap_Struct(klass, NULL, local_memcache_free, lmc);
|
36
54
|
}
|
37
55
|
|
38
56
|
/* :nodoc: */
|
39
|
-
static VALUE LocalMemCache__clear_namespace(VALUE klass, VALUE ns,
|
57
|
+
static VALUE LocalMemCache__clear_namespace(VALUE klass, VALUE ns,
|
58
|
+
VALUE repair) {
|
40
59
|
lmc_error_t e;
|
41
60
|
if (!local_memcache_clear_namespace(rstring_ptr(ns), bool_value(repair), &e)) {
|
42
|
-
raise_exception(
|
61
|
+
raise_exception(&e);
|
43
62
|
}
|
44
63
|
return Qnil;
|
45
64
|
}
|
46
65
|
|
66
|
+
/* :nodoc: */
|
67
|
+
static VALUE LocalMemCache__check_namespace(VALUE klass, VALUE ns) {
|
68
|
+
lmc_error_t e;
|
69
|
+
if (!local_memcache_check_namespace(rstring_ptr(ns), &e)) {
|
70
|
+
raise_exception(&e);
|
71
|
+
}
|
72
|
+
return Qnil;
|
73
|
+
}
|
74
|
+
|
75
|
+
/* :nodoc: */
|
76
|
+
static VALUE LocalMemCache__enable_test_crash(VALUE klass) {
|
77
|
+
srand(getpid());
|
78
|
+
lmc_test_crash_enabled = 1;
|
79
|
+
return Qnil;
|
80
|
+
}
|
81
|
+
|
82
|
+
/* :nodoc: */
|
83
|
+
static VALUE LocalMemCache__disable_test_crash(VALUE klass) {
|
84
|
+
lmc_test_crash_enabled = 0;
|
85
|
+
return Qnil;
|
86
|
+
}
|
87
|
+
|
47
88
|
/* :nodoc: */
|
48
89
|
local_memcache_t *get_LocalMemCache(VALUE obj) {
|
49
90
|
local_memcache_t *lmc;
|
@@ -59,8 +100,12 @@ local_memcache_t *get_LocalMemCache(VALUE obj) {
|
|
59
100
|
* Retrieve value from hashtable.
|
60
101
|
*/
|
61
102
|
static VALUE LocalMemCache__get(VALUE obj, VALUE key) {
|
62
|
-
|
63
|
-
|
103
|
+
size_t l;
|
104
|
+
const char* r = __local_memcache_get(get_LocalMemCache(obj),
|
105
|
+
rstring_ptr(key), rstring_length(key), &l);
|
106
|
+
VALUE rr = lmc_ruby_string2(r, l);
|
107
|
+
lmc_unlock_shm_region("local_memcache_get", get_LocalMemCache(obj));
|
108
|
+
return rr;
|
64
109
|
}
|
65
110
|
|
66
111
|
/*
|
@@ -73,8 +118,9 @@ static VALUE LocalMemCache__get(VALUE obj, VALUE key) {
|
|
73
118
|
|
74
119
|
static VALUE LocalMemCache__set(VALUE obj, VALUE key, VALUE value) {
|
75
120
|
local_memcache_t *lmc = get_LocalMemCache(obj);
|
76
|
-
if (!local_memcache_set(lmc, rstring_ptr(key),
|
77
|
-
|
121
|
+
if (!local_memcache_set(lmc, rstring_ptr(key), rstring_length(key),
|
122
|
+
rstring_ptr(value), rstring_length(value))) {
|
123
|
+
raise_exception(&lmc->error);
|
78
124
|
}
|
79
125
|
return Qnil;
|
80
126
|
}
|
@@ -87,7 +133,7 @@ static VALUE LocalMemCache__set(VALUE obj, VALUE key, VALUE value) {
|
|
87
133
|
*/
|
88
134
|
static VALUE LocalMemCache__delete(VALUE obj, VALUE key) {
|
89
135
|
return local_memcache_delete(get_LocalMemCache(obj),
|
90
|
-
rstring_ptr(key));
|
136
|
+
rstring_ptr(key), rstring_length(key));
|
91
137
|
return Qnil;
|
92
138
|
}
|
93
139
|
|
@@ -98,22 +144,66 @@ static VALUE LocalMemCache__delete(VALUE obj, VALUE key) {
|
|
98
144
|
* Releases hashtable.
|
99
145
|
*/
|
100
146
|
static VALUE LocalMemCache__close(VALUE obj) {
|
101
|
-
|
147
|
+
lmc_error_t e;
|
148
|
+
if (!local_memcache_free(get_LocalMemCache(obj), &e)) raise_exception(&e);
|
102
149
|
return Qnil;
|
103
150
|
}
|
104
151
|
|
105
|
-
|
152
|
+
typedef struct {
|
153
|
+
VALUE ary;
|
154
|
+
} lmc_ruby_iter_collect_keys;
|
155
|
+
|
156
|
+
int lmc_ruby_iter(void *ctx, const char* key, const char* value) {
|
157
|
+
lmc_ruby_iter_collect_keys *data = ctx;
|
158
|
+
rb_ary_push(data->ary, lmc_ruby_string(key));
|
159
|
+
return 1;
|
160
|
+
}
|
161
|
+
|
162
|
+
static VALUE __LocalMemCache__keys(VALUE d) {
|
163
|
+
VALUE obj = rb_ary_entry(d, 0);
|
164
|
+
VALUE r = rb_ary_entry(d, 1);
|
165
|
+
lmc_ruby_iter_collect_keys data;
|
166
|
+
data.ary = r;
|
167
|
+
int success = local_memcache_iterate(get_LocalMemCache(obj),
|
168
|
+
(void *) &data, lmc_ruby_iter);
|
169
|
+
if (!success) { return Qnil; }
|
170
|
+
}
|
171
|
+
|
172
|
+
/*
|
173
|
+
* call-seq:
|
174
|
+
* lmc.keys() -> array or nil
|
175
|
+
*
|
176
|
+
* Returns a list of keys.
|
177
|
+
*/
|
178
|
+
static VALUE LocalMemCache__keys(VALUE obj) {
|
179
|
+
VALUE d = rb_ary_new();
|
180
|
+
rb_ary_push(d, obj);
|
181
|
+
rb_ary_push(d, rb_ary_new());
|
182
|
+
int error = 0;
|
183
|
+
rb_protect(__LocalMemCache__keys, d, &error);
|
184
|
+
if (error) {
|
185
|
+
lmc_unlock_shm_region("local_memcache_iterate", get_LocalMemCache(obj));
|
186
|
+
rb_exc_raise(ruby_errinfo);
|
187
|
+
}
|
188
|
+
return rb_ary_entry(d, 1);
|
189
|
+
}
|
106
190
|
|
107
191
|
void Init_rblocalmemcache() {
|
108
|
-
LocalMemCacheError = rb_define_class("LocalMemCacheError", rb_eStandardError);
|
109
192
|
LocalMemCache = rb_define_class("LocalMemCache", rb_cObject);
|
110
193
|
rb_define_singleton_method(LocalMemCache, "_new", LocalMemCache__new2, 2);
|
111
194
|
rb_define_singleton_method(LocalMemCache, "_clear_namespace",
|
112
195
|
LocalMemCache__clear_namespace, 2);
|
196
|
+
rb_define_singleton_method(LocalMemCache, "check_namespace",
|
197
|
+
LocalMemCache__check_namespace, 1);
|
198
|
+
rb_define_singleton_method(LocalMemCache, "disable_test_crash",
|
199
|
+
LocalMemCache__disable_test_crash, 0);
|
200
|
+
rb_define_singleton_method(LocalMemCache, "enable_test_crash",
|
201
|
+
LocalMemCache__enable_test_crash, 0);
|
113
202
|
rb_define_method(LocalMemCache, "get", LocalMemCache__get, 1);
|
114
203
|
rb_define_method(LocalMemCache, "[]", LocalMemCache__get, 1);
|
115
204
|
rb_define_method(LocalMemCache, "delete", LocalMemCache__delete, 1);
|
116
205
|
rb_define_method(LocalMemCache, "set", LocalMemCache__set, 2);
|
117
206
|
rb_define_method(LocalMemCache, "[]=", LocalMemCache__set, 2);
|
207
|
+
rb_define_method(LocalMemCache, "keys", LocalMemCache__keys, 0);
|
118
208
|
rb_define_method(LocalMemCache, "close", LocalMemCache__close, 0);
|
119
209
|
}
|
data/src/tests/alloc
CHANGED
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
D=`dirname $0`
|
3
|
+
DIR=`cd $D; pwd`
|
4
|
+
script=$DIR/allocfailure.rb
|
5
|
+
|
6
|
+
export CFLAGS=-DDO_TEST_ALLOC_FAILURE
|
7
|
+
make -C .. clean && make -C .. && make -C ../ruby-binding &&
|
8
|
+
ruby extconf.rb && make clean && make
|
9
|
+
|
10
|
+
if test "x$1" = "x-d"; then
|
11
|
+
irb -r $script
|
12
|
+
else
|
13
|
+
#valgrind --leak-check=full --tool=memcheck ruby $script
|
14
|
+
ruby $script
|
15
|
+
fi
|
@@ -0,0 +1,20 @@
|
|
1
|
+
$DIR=File.dirname(__FILE__)
|
2
|
+
['.', '..', '../ruby-binding/'].each {|p| $:.unshift File.join($DIR, p) }
|
3
|
+
|
4
|
+
require 'localmemcache'
|
5
|
+
|
6
|
+
LocalMemCache.clear_namespace("alloc-failure-test", true);
|
7
|
+
|
8
|
+
|
9
|
+
LocalMemCache.enable_test_crash
|
10
|
+
$lm2 = LocalMemCache.new :namespace=>"alloc-failure-test"
|
11
|
+
2000000.times {
|
12
|
+
begin
|
13
|
+
r = rand(10000).to_s
|
14
|
+
$lm2.set(r, r)
|
15
|
+
$lm2.get(r)
|
16
|
+
rescue Exception => e
|
17
|
+
puts "e: #{e.to_s}"
|
18
|
+
end
|
19
|
+
}
|
20
|
+
|
data/src/tests/bench
CHANGED
File without changes
|