localmemcache 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  *.o
2
2
  *.a
3
+ .tmp*
3
4
  core
4
5
  vgcore*
5
6
  core*
data/Rakefile CHANGED
@@ -25,11 +25,7 @@ task :changelog do
25
25
  end
26
26
 
27
27
  task :sanity_test do
28
- sh "./configure && make -C src clean && make -C src && " +
29
- "(cd src/ruby-binding; ruby extconf.rb) && " +
30
- "make -C src/ruby-binding/ && " +
31
- "(cd src/tests; ruby extconf.rb) && " +
32
- "make -C src/tests/ && ./src/tests/lmc "
28
+ sh "./src/tests/sanity-test"
33
29
  end
34
30
 
35
31
  task :site_rdoc do
data/THANKS CHANGED
@@ -1 +1,2 @@
1
- Armin Roehrl: For his thoughts and support
1
+ * Armin Roehrl: For his thoughts and support
2
+ * Paul Mineiro: Bug reports and improving iteration speed
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.2
1
+ 0.4.3
data/site/index.html CHANGED
@@ -17,7 +17,35 @@ speed</b>.
17
17
  Since version 0.3.0 it supports <b>persistence</b>, also making it <b>a fast
18
18
  alternative to GDBM, Berkeley DB, and Tokyo Cabinet</b>.
19
19
 
20
- <h2>Version 0.4.2: Improving Append Performance (2009-08-10)</h2>
20
+ <h2>Version 0.4.3: Improving Iteration and bugfixes (2009-10-02)</h2>
21
+
22
+ Fixes by <b>Paul Mineiro</b> (thanks!):
23
+ <li><b>iteration speedup</b>: each_pair is now a lot faster when dealing with a
24
+ large dictionary. Eg: For 400k elements it's now about <b>14 times
25
+ faster</b>.</li>
26
+ <li><b>fix for leak</b> in local_memcache_free()</li>
27
+ <li><b>C API</b>: Some C API functions were not yet covered by tests. This
28
+ has been fixed.</li>
29
+ <p>
30
+
31
+ Other fixes:
32
+ <li><b>autorepair</b> fixed: Autorepair of namespaces did sometimes wrongly
33
+ report corrupt namespaces.
34
+ <li><b>:min_alloc_size</b> now also works on Ruby 1.9.</li>
35
+
36
+
37
+ <script type="text/javascript">
38
+ // <![CDATA[
39
+ function show_complete_history() {
40
+ var e = document.getElementById('old-news')
41
+ e.style.display = e.style.display == "none" ? "block" : "none";
42
+ return false;
43
+ }
44
+ // ]]
45
+ </script>
46
+
47
+ <a href="#" onclick="show_complete_history()" />Previous Releases</a>
48
+ <div id='old-news' style='display:none'><h2>Version 0.4.2: Improving Append Performance (2009-08-10)</h2>
21
49
 
22
50
  In 0.4.2 the <b>:min_alloc_size</b> parameter was introduced to help with use
23
51
  cases that intend to use a hash table with growing values. This is
@@ -26,6 +54,7 @@ with a large list of unusable free blocks. By setting the
26
54
  <b>:min_alloc_size</b> parameter you help the allocator to plan better
27
55
  ahead. (<a href="http://localmemcache.rubyforge.org/doc/classes/LocalMemCache.html#M000001">more</a>)
28
56
 
57
+ </div>
29
58
 
30
59
  <h2>Key features as of 0.4.0 (2009-05-16)</h2>
31
60
  <li><a href="#performance">blazingly fast</a></li>
@@ -41,7 +70,7 @@ ahead. (<a href="http://localmemcache.rubyforge.org/doc/classes/LocalMemCache.ht
41
70
  Note for <b>OS X</b>: <b>OS X</b> disqualifies as <b>HFS+</b> doesn't
42
71
  have sparse files and <b>sem_timedwait</b>() and <b>sem_getvalue</b>() aren't
43
72
  supported as well.<br>
44
- Note for <b>FreeBSD</b>: It has been reported that Localmemcache
73
+ Note for <b>FreeBSD</b>: It has been reported that localmemcache
45
74
  sometimes hangs there, it is not yet clear what the problem is.
46
75
 
47
76
  <h2>Install</h2>
@@ -96,7 +125,7 @@ Ruby Hash of Strings: <b>4,963.313</b> ms
96
125
 
97
126
  <p>
98
127
 
99
- So, on my machine, using <b>Localmemcache</b> 0.4.0 to store key-value
128
+ So, on my machine, using <b>localmemcache</b> 0.4.0 to store key-value
100
129
  data on disk is about <b>10</b>% slower than keeping them in memory in a
101
130
  Ruby hash of strings. It's about <b>40</b>% faster than <b>Tokyo Cabinet</b>
102
131
  (which offers features similar to <b>LocalMemCache</b>).
@@ -109,15 +138,23 @@ can be retrieved by executing</p>
109
138
  <pre><code>git clone git://github.com/sck/localmemcache.git
110
139
  </code></pre>
111
140
 
141
+ <h2>Caveats</h2>
142
+
143
+ <li>Localmemcache's .lmc files are not binary compatible, they are essentially
144
+ memory mapped c structs</li>
145
+ <li>Because of the convenient auto repair feature after a lock timeout,
146
+ localmemcache is allergic to SIGSTOP (If you manage to SIGSTOP a process
147
+ while localmemcache is currently possessing a lock, that is)</li>
148
+
112
149
  <h2>Who uses Localmemcache?</h2>
113
150
 
114
- <a href="http://www.personifi.com">Personifi</a> use Localmemcache to
151
+ <a href="http://www.personifi.com">Personifi</a> use localmemcache to
115
152
  serve billions of hits each month. Armin Roehrl: "we use
116
153
  localmemcache because it solves one problem very well and we love it!"
117
154
 
118
155
  <h2>Tips for backups</h2>
119
156
 
120
- Note that you cannot copy Localmemcache's .lmc files while other
157
+ Note that you cannot copy localmemcache's .lmc files while other
121
158
  processes are making changes to the data, this will likely result in a
122
159
  corrupt backup. So you need to make sure that none of your processes are
123
160
  writing during the time you do an backup. As for copying sparse files,
data/src/lmc_hashtable.c CHANGED
@@ -185,25 +185,28 @@ int ht_delete(void *base, va_ht_hash_t va_ht, const char *key, size_t n_key) {
185
185
  }
186
186
 
187
187
  int ht_hash_iterate(void *base, va_ht_hash_t va_ht, void *ctx,
188
- size_t *ofs, LMC_ITERATOR_P(iter)) {
188
+ ht_iter_status_t *s, LMC_ITERATOR_P(iter)) {
189
189
  va_ht_hash_entry_t va_hr;
190
190
  ht_hash_entry_t *hr = &lmc_null_node;
191
191
  ht_hash_t *ht = base + va_ht;
192
192
  size_t k;
193
193
  size_t item_c = 0;
194
194
  size_t slice_counter = 0;
195
- for (k = 0; k < LMC_HT_BUCKETS; k++) {
195
+ for (k = s->bucket_ofs; k < LMC_HT_BUCKETS; k++) {
196
196
  for (va_hr = ht->va_buckets[k]; va_hr != 0 && hr != NULL;
197
197
  va_hr = hr->va_next) {
198
198
  hr = va_hr ? base + va_hr : 0;
199
- if (*ofs < ++item_c) {
199
+ if (s->ofs < ++item_c) {
200
200
  iter(ctx, base + hr->va_key, base + hr->va_value);
201
201
  if (++slice_counter > 999) {
202
- *ofs = item_c;
202
+ s->ofs = item_c;
203
203
  return 2;
204
204
  }
205
205
  }
206
206
  }
207
+ ++(s->bucket_ofs);
208
+ s->ofs = 0;
209
+ item_c = 0;
207
210
  }
208
211
  return 1;
209
212
  }
data/src/lmc_hashtable.h CHANGED
@@ -16,6 +16,11 @@ typedef struct {
16
16
  va_string_t va_value;
17
17
  } ht_hash_entry_t;
18
18
 
19
+ typedef struct {
20
+ size_t ofs;
21
+ size_t bucket_ofs;
22
+ } ht_iter_status_t;
23
+
19
24
  #define LMC_HT_BUCKETS 20731
20
25
  #define LMC_ITERATOR_P(n) int ((n)) \
21
26
  (void *ctx, const char *key, const char *value)
@@ -35,7 +40,7 @@ const char *ht_get(void *base, va_ht_hash_t va_ht, const char *key, size_t n_key
35
40
  size_t *n_value);
36
41
  int ht_delete(void *base, va_ht_hash_t va_ht, const char *key, size_t n_key);
37
42
  int ht_hash_destroy(void *base, va_ht_hash_t ht);
38
- int ht_hash_iterate(void *base, va_ht_hash_t ht, void *ctx, size_t *ofs,
43
+ int ht_hash_iterate(void *base, va_ht_hash_t ht, void *ctx, ht_iter_status_t *s,
39
44
  LMC_ITERATOR_P(iter));
40
45
  int ht_random_pair(void *base, va_ht_hash_t va_ht, char **r_key,
41
46
  size_t *n_key, char **r_value, size_t *n_value);
data/src/lmc_lock.c CHANGED
@@ -32,13 +32,19 @@ lmc_lock_t *lmc_lock_init(const char *ns, int init, lmc_error_t *e) {
32
32
  return l;
33
33
  }
34
34
 
35
+ void lmc_lock_free(lmc_lock_t* l) {
36
+ if (!l) return;
37
+ if (l->sem) { sem_close(l->sem); }
38
+ free(l);
39
+ }
40
+
35
41
  int lmc_clear_namespace_lock(const char *ns) {
36
42
  char namespace[1024];
37
43
  lmc_namespacify(namespace, ns);
38
44
  lmc_error_t e;
39
45
  lmc_lock_t *l = lmc_lock_init(namespace, 1, &e);
40
46
  lmc_lock_repair(l);
41
- free(l);
47
+ lmc_lock_free(l);
42
48
  return 1;
43
49
  }
44
50
 
@@ -72,7 +78,7 @@ int lmc_sem_timed_wait_mandatory(lmc_lock_t* l) {
72
78
  #else
73
79
  struct timespec ts;
74
80
  clock_gettime(CLOCK_REALTIME, &ts);
75
- ts.tv_sec += 20;
81
+ ts.tv_sec += 60;
76
82
  return sem_timedwait(l->sem, &ts);
77
83
  #endif
78
84
  }
data/src/lmc_lock.h CHANGED
@@ -13,6 +13,7 @@ typedef struct {
13
13
  } lmc_lock_t;
14
14
 
15
15
  lmc_lock_t *lmc_lock_init(const char *namespace, int init, lmc_error_t *e);
16
+ void lmc_lock_free(lmc_lock_t* l);
16
17
  int lmc_lock_obtain(const char *where, lmc_lock_t* l, lmc_error_t *e);
17
18
  int lmc_lock_obtain_mandatory(const char *where, lmc_lock_t* l, lmc_error_t *e);
18
19
  int lmc_lock_release(const char *where, lmc_lock_t* l, lmc_error_t *e);
data/src/lmc_valloc.c CHANGED
@@ -360,11 +360,11 @@ void lmc_free(void *base, size_t chunk) {
360
360
  __lmc_free(base, va_used_chunk, uc_size);
361
361
  }
362
362
 
363
- int lmc_um_getbit(char *bf, int i) {
363
+ int lmc_um_getbit(char *bf, size_t i) {
364
364
  bf += i / 8; return (*bf & (1 << (i % 8))) != 0;
365
365
  }
366
366
 
367
- void lmc_um_setbit(char *bf, int i, int v) {
367
+ void lmc_um_setbit(char *bf, size_t i, size_t v) {
368
368
  bf += i / 8;
369
369
  if (v) *bf |= 1 << (i % 8);
370
370
  else *bf &= ~(1 << (i % 8));
@@ -382,7 +382,8 @@ int lmc_um_find_leaks(void *base, char *bf) {
382
382
  for (i = 0; i < md->total_size; ++i) {
383
383
  if (!gap) {
384
384
  size_t *b = (void *)bf + i / 8;
385
- while (*b == m && i < md->total_size - sizeof(size_t)) {
385
+ size_t ee = md->total_size - sizeof(size_t) * 8;
386
+ while (*b == m && i < ee) {
386
387
  i += sizeof(size_t) * 8; b++;
387
388
  }
388
389
  }
@@ -415,10 +416,13 @@ int lmc_um_check_unmarked(void *base, char *bf, size_t va, size_t size) {
415
416
  size_t end = va + size;
416
417
  for (i = va; i < va + size; ++i) {
417
418
  size_t *b = (void *)bf + i / 8;
418
- while (*b == n && i < end - sizeof(size_t)) {
419
+ size_t ee = end - sizeof(size_t) * 8;
420
+ while (*b == n && i < ee) {
419
421
  i += sizeof(size_t) * 8; b++;
420
422
  }
421
- if (lmc_um_getbit(bf, i) != 0) { return 0; }
423
+ if (lmc_um_getbit(bf, i) != 0) {
424
+ return 0;
425
+ }
422
426
  }
423
427
  return 1;
424
428
  }
@@ -428,11 +432,15 @@ int lmc_um_mark(void *base, char *bf, size_t va, size_t size) {
428
432
  lmc_mem_descriptor_t *md = base;
429
433
  if ((va > sizeof(lmc_mem_descriptor_t)) &&
430
434
  (!lmc_is_va_valid(base, va) || !lmc_is_va_valid(base, va + size))) {
431
- printf("[localmemcache] Error: VA start out of range: "
435
+ fprintf(stderr, "[localmemcache] Error: VA start out of range: "
432
436
  "va: %zd - %zd max %zd!\n", va, va + size, md->total_size);
433
437
  return 0;
434
438
  }
435
- if (!lmc_um_check_unmarked(base, bf, va, size)) return 0;
439
+ if (!lmc_um_check_unmarked(base, bf, va, size)) {
440
+ fprintf(stderr, "[localmemcache] Error: Part of a block to be "
441
+ "marked used is used already (va: %zd s: %zd) !\n", va, size);
442
+ return 0;
443
+ }
436
444
  for (i = va; i < va + size; ++i) {
437
445
  if (i % 8 == 0) {
438
446
  size_t b_start = i / 8;
@@ -450,7 +458,8 @@ int lmc_um_mark(void *base, char *bf, size_t va, size_t size) {
450
458
  int lmc_um_mark_allocated(void *base, char *bf, size_t va) {
451
459
  size_t real_va = va - sizeof(size_t);
452
460
  size_t s = *(size_t *)(base + real_va);
453
- return lmc_um_mark(base, bf, real_va, s);
461
+ int r = lmc_um_mark(base, bf, real_va, s);
462
+ return r;
454
463
  }
455
464
 
456
465
  char *lmc_um_new_mem_usage_bitmap(void *base) {
data/src/localmemcache.c CHANGED
@@ -56,6 +56,8 @@ int lmc_namespace_or_filename(char *result, const char* ons, const char *ofn,
56
56
  return 0;
57
57
  }
58
58
 
59
+ void lmc_checkize(char *result, char *s) { snprintf(result, 1023, "%s-check", s); }
60
+
59
61
  int local_memcache_drop_namespace(const char *namespace, const char *filename,
60
62
  int force, lmc_error_t *e) {
61
63
  char clean_ns[1024];
@@ -65,13 +67,13 @@ int local_memcache_drop_namespace(const char *namespace, const char *filename,
65
67
  if (force) {
66
68
  lmc_lock_t *l = lmc_lock_init((char *)clean_ns, 1, e);
67
69
  lmc_lock_repair(l);
68
- free(l);
70
+ lmc_lock_free(l);
69
71
  char check_lock_name[1024];
70
- snprintf((char *)&check_lock_name, 1023, "%s-check", (char *)clean_ns);
72
+ lmc_checkize(check_lock_name, clean_ns);
71
73
  lmc_lock_t *check_l;
72
74
  check_l = lmc_lock_init(check_lock_name, 1, e);
73
75
  lmc_lock_repair(check_l);
74
- free(check_l);
76
+ lmc_lock_free(check_l);
75
77
  }
76
78
  return 1;
77
79
  }
@@ -159,9 +161,45 @@ local_memcache_t *local_memcache_create(const char *namespace,
159
161
  return __local_memcache_create((char *)clean_ns, si, min_alloc_size, 0, 0, e);
160
162
  }
161
163
 
164
+ int lmc_lock_shm_region(const char *who, local_memcache_t *lmc) {
165
+ int r;
166
+ int retry_counter = 0;
167
+ retry:
168
+ if (retry_counter++ > 10) {
169
+ fprintf(stderr, "[localmemcache] Too many retries: "
170
+ "Cannot repair namespace '%s'\n", lmc->namespace);
171
+ return 0;
172
+ }
173
+ r = lmc_lock_obtain(who, lmc->lock, &lmc->error);
174
+ if (!r && (strcmp(lmc->error.error_type, "LockTimedOut") == 0)) {
175
+ if (__local_memcache_check_namespace(lmc->namespace,
176
+ &lmc->error)) goto retry;
177
+ fprintf(stderr, "[localmemcache] Cannot repair namespace '%s'\n",
178
+ lmc->namespace);
179
+ }
180
+ if (!r) return 0;
181
+ if (!lmc_set_lock_flag(lmc->base, &lmc->error)) {
182
+ lmc_lock_release(who, lmc->lock, &lmc->error);
183
+ return 0;
184
+ }
185
+ return 1;
186
+ }
187
+
188
+ int __local_memcache_free(local_memcache_t *lmc, lmc_error_t *e, int lock) {
189
+ if (lock && !lmc_lock_shm_region("local_memcache_free", lmc)) return 0;
190
+ int r = ht_hash_destroy(lmc->base, lmc->va_hash);
191
+ if (lock && !lmc_unlock_shm_region("local_memcache_free", lmc)) return 0;
192
+ lmc_shm_destroy(lmc->shm, e);
193
+ free(lmc->namespace);
194
+ lmc_lock_free(lmc->lock);
195
+ free(lmc);
196
+ return r;
197
+ }
198
+
162
199
  int __local_memcache_check_namespace(const char *clean_ns, lmc_error_t *e) {
200
+ int repair_failed = 0;
163
201
  char check_lock_name[1024];
164
- snprintf((char *)check_lock_name, 1023, "%s-check", (char *)clean_ns);
202
+ lmc_checkize(check_lock_name, (char *)clean_ns);
165
203
 
166
204
  if (!lmc_does_namespace_exist((char *)clean_ns)) {
167
205
  lmc_clear_namespace_lock(check_lock_name);
@@ -216,17 +254,21 @@ release:
216
254
  release_but_no_lock_correction:
217
255
  local_memcache_free(lmc, e);
218
256
  lmc_lock_release("local_memcache_check_namespace", check_l, e);
219
- free(check_l);
257
+ lmc_lock_free(check_l);
220
258
  return 1;
221
259
  failed:
260
+ repair_failed = 1;
222
261
  lmc_handle_error_with_err_string("local_memcache_check_namespace",
223
262
  "Unable to recover namespace", "RecoveryFailed", e);
224
- local_memcache_free(lmc, e);
263
+ __local_memcache_free(lmc, e, 0);
225
264
  lmc_lock_release("local_memcache_check_namespace", check_l, e);
265
+ fprintf(stderr, "[localmemcache] Recovery failed!\n");
226
266
  check_lock_failed:
227
- free(check_l);
228
- fprintf(stderr, "[localmemcache] Failed to repair namespace '%s'\n",
229
- clean_ns);
267
+ lmc_lock_free(check_l);
268
+ if (!repair_failed) {
269
+ fprintf(stderr, "[localmemcache] Failed to obtain the 'check lock' to repair "
270
+ "namespace '%s'\n", clean_ns);
271
+ }
230
272
  return 0;
231
273
  }
232
274
 
@@ -238,30 +280,6 @@ int local_memcache_check_namespace(const char *namespace, const char *filename,
238
280
  return __local_memcache_check_namespace(clean_ns, e);
239
281
  }
240
282
 
241
- int lmc_lock_shm_region(const char *who, local_memcache_t *lmc) {
242
- int r;
243
- int retry_counter = 0;
244
- retry:
245
- if (retry_counter++ > 10) {
246
- fprintf(stderr, "[localmemcache] Too many retries: "
247
- "Cannot repair namespace '%s'\n", lmc->namespace);
248
- return 0;
249
- }
250
- r = lmc_lock_obtain(who, lmc->lock, &lmc->error);
251
- if (!r && (strcmp(lmc->error.error_type, "LockTimedOut") == 0)) {
252
- if (__local_memcache_check_namespace(lmc->namespace,
253
- &lmc->error)) goto retry;
254
- fprintf(stderr, "[localmemcache] Cannot repair namespace '%s'\n",
255
- lmc->namespace);
256
- }
257
- if (!r) return 0;
258
- if (!lmc_set_lock_flag(lmc->base, &lmc->error)) {
259
- lmc_lock_release(who, lmc->lock, &lmc->error);
260
- return 0;
261
- }
262
- return 1;
263
- }
264
-
265
283
  int lmc_unlock_shm_region(const char *who, local_memcache_t *lmc) {
266
284
  int r = 1;
267
285
  if (!lmc_release_lock_flag(lmc->base, &lmc->error)) r = 0;
@@ -279,8 +297,11 @@ const char *__local_memcache_get(local_memcache_t *lmc,
279
297
  char *local_memcache_get_new(local_memcache_t *lmc,
280
298
  const char *key, size_t n_key, size_t *n_value) {
281
299
  const char *r = __local_memcache_get(lmc, key, n_key, n_value);
282
- char *new_s = malloc(*n_value);
283
- memcpy(new_s, r, *n_value);
300
+ char *new_s = 0;
301
+ if (r) {
302
+ new_s = malloc(*n_value);
303
+ memcpy(new_s, r, *n_value);
304
+ }
284
305
  if (!lmc_unlock_shm_region("local_memcache_get_new", lmc)) return 0;
285
306
  return new_s;
286
307
  }
@@ -296,11 +317,13 @@ int local_memcache_random_pair_new(local_memcache_t *lmc,
296
317
  char **r_key, size_t *n_key, char **r_value, size_t *n_value) {
297
318
  char *k;
298
319
  char *v;
320
+ (*r_key) = 0;
321
+ (*r_value) = 0;
299
322
  if (__local_memcache_random_pair(lmc, &k, n_key, &v, n_value)) {
300
- char *new_k = malloc(*n_key);
301
- memcpy(new_k, k, *n_key);
302
- char *new_v = malloc(*n_value);
303
- memcpy(new_v, v, *n_value);
323
+ (*r_key) = malloc(*n_key);
324
+ memcpy((*r_key), k, *n_key);
325
+ (*r_value) = malloc(*n_value);
326
+ memcpy((*r_value), v, *n_value);
304
327
  }
305
328
  if (!lmc_unlock_shm_region("local_memcache_random_pair", lmc)) return 0;
306
329
  return 1;
@@ -334,19 +357,21 @@ int local_memcache_delete(local_memcache_t *lmc, char *key, size_t n_key) {
334
357
  }
335
358
 
336
359
  int local_memcache_free(local_memcache_t *lmc, lmc_error_t *e) {
337
- if (!lmc_lock_shm_region("local_memcache_free", lmc)) return 0;
338
- int r = ht_hash_destroy(lmc->base, lmc->va_hash);
339
- if (!lmc_unlock_shm_region("local_memcache_free", lmc)) return 0;
340
- lmc_shm_destroy(lmc->shm, e);
341
- free(lmc->namespace);
342
- free(lmc->lock);
343
- return r;
360
+ return __local_memcache_free(lmc, e, 1);
344
361
  }
345
362
 
346
363
  int local_memcache_iterate(local_memcache_t *lmc, void *ctx,
347
- size_t *ofs, LMC_ITERATOR_P(iter)) {
364
+ ht_iter_status_t *s, LMC_ITERATOR_P(iter)) {
348
365
  if (!lmc_lock_shm_region("local_memcache_iterate", lmc)) return 0;
349
- int r = ht_hash_iterate(lmc->base, lmc->va_hash, ctx, ofs, iter);
366
+ int r = ht_hash_iterate(lmc->base, lmc->va_hash, ctx, s, iter);
350
367
  if (!lmc_unlock_shm_region("local_memcache_iterate", lmc)) return 0;
351
368
  return r;
352
369
  }
370
+
371
+ int local_memcache_check_consistency(local_memcache_t *lmc, lmc_error_t *e) {
372
+ lmc_mem_descriptor_t *md = lmc->base;
373
+ if (!lmc_lock_shm_region("local_memcache_check_consistency", lmc)) return 0;
374
+ int r = ht_check_memory(lmc->base, md->va_hash);
375
+ if (!lmc_unlock_shm_region("local_memcache_check_consistency", lmc)) return 0;
376
+ return r;
377
+ }
data/src/localmemcache.h CHANGED
@@ -170,7 +170,9 @@ int local_memcache_free(local_memcache_t *lmc, lmc_error_t *e);
170
170
  *
171
171
  * local_memcache_t *lmc;
172
172
  * collector_t c;
173
- * local_memcache_iterate(lmc, (void *) &c, my_collect);
173
+ * ht_iter_status_t s;
174
+ * memset(s, sizeof(ht_iter_status_t));
175
+ * local_memcache_iterate(lmc, (void *) &c, &s, my_collect);
174
176
  *
175
177
  * The memory pool will be locked while iteration takes place, so try to make
176
178
  * sure you can iterate within under 2 seconds otherwise other waiting
@@ -178,7 +180,7 @@ int local_memcache_free(local_memcache_t *lmc, lmc_error_t *e);
178
180
  * triggering the automatic recovery.)
179
181
  *
180
182
  */
181
- int local_memcache_iterate(local_memcache_t *lmc, void *ctx, size_t *ofs,
183
+ int local_memcache_iterate(local_memcache_t *lmc, void *ctx, ht_iter_status_t *s,
182
184
  LMC_ITERATOR_P(iter));
183
185
 
184
186
  /*
@@ -218,6 +220,7 @@ int local_memcache_drop_namespace(const char *namespace, const char *filename,
218
220
  int local_memcache_check_namespace(const char *namespace, const char *filename,
219
221
  lmc_error_t *e);
220
222
 
223
+
221
224
  /* internal, do not use */
222
225
  const char *__local_memcache_get(local_memcache_t *lmc,
223
226
  const char *key, size_t n_key, size_t *n_value);
@@ -228,4 +231,8 @@ int __local_memcache_random_pair(local_memcache_t *lmc,
228
231
 
229
232
  /* internal, do not use */
230
233
  int lmc_unlock_shm_region(const char *who, local_memcache_t *lmc);
234
+
235
+ /* internal, do not use */
236
+ int local_memcache_check_consistency(local_memcache_t *lmc, lmc_error_t *e);
237
+
231
238
  #endif
@@ -29,7 +29,7 @@
29
29
  #endif
30
30
 
31
31
  /* :nodoc: */
32
- long long_value(VALUE i) { return NUM2LONG(rb_Integer(i)); }
32
+ long long_value(VALUE i) { return NIL_P(i) ? 0 : NUM2LONG(rb_Integer(i)); }
33
33
  /* :nodoc: */
34
34
  double double_value(VALUE i) { return NUM2DBL(i); }
35
35
  /* :nodoc: */
@@ -322,10 +322,11 @@ static VALUE __LocalMemCache__keys(VALUE d) {
322
322
  lmc_ruby_iter_collect_keys data;
323
323
  data.ary = r;
324
324
  int success = 2;
325
- size_t ofs = 0;
325
+ ht_iter_status_t s;
326
+ memset(&s, 0x0, sizeof(ht_iter_status_t));
326
327
  while (success == 2) {
327
328
  success = local_memcache_iterate(get_LocalMemCache(obj),
328
- (void *) &data, &ofs, lmc_ruby_iter);
329
+ (void *) &data, &s, lmc_ruby_iter);
329
330
  }
330
331
  if (!success) { return Qnil; }
331
332
  return Qnil;
@@ -366,13 +367,14 @@ int lmc_ruby_iter_collect_pairs(void *ctx, const char* key, const char* value) {
366
367
  static VALUE __LocalMemCache__each_pair(VALUE d) {
367
368
  VALUE obj = rb_ary_entry(d, 0);
368
369
  int success = 2;
369
- size_t ofs = 0;
370
+ ht_iter_status_t s;
371
+ memset(&s, 0x0, sizeof(ht_iter_status_t));
370
372
  while (success == 2) {
371
373
  VALUE r = rb_ary_new();
372
374
  lmc_ruby_iter_collect_pairs_t data;
373
375
  data.ary = r;
374
376
  success = local_memcache_iterate(get_LocalMemCache(obj),
375
- (void *) &data, &ofs, lmc_ruby_iter_collect_pairs);
377
+ (void *) &data, &s, lmc_ruby_iter_collect_pairs);
376
378
  long i;
377
379
  for (i = 0; i < RARRAY_LEN(r); i++) {
378
380
  rb_yield(RARRAY_PTR(r)[i]);
@@ -412,6 +414,17 @@ static VALUE LocalMemCache__size(VALUE obj) {
412
414
  return rb_int2big(ht->size);
413
415
  }
414
416
 
417
+ /*
418
+ * internal, do not use
419
+ */
420
+ static VALUE LocalMemCache__check_consistency(VALUE obj) {
421
+ lmc_error_t e;
422
+ rb_lmc_handle_t *h;
423
+ Data_Get_Struct(obj, rb_lmc_handle_t, h);
424
+ return local_memcache_check_consistency(rb_lmc_check_handle_access(h), &e) ?
425
+ Qtrue : Qfalse;
426
+ }
427
+
415
428
  /*
416
429
  * Document-class: LocalMemCache
417
430
  *
@@ -487,6 +500,8 @@ void Init_rblocalmemcache() {
487
500
  0);
488
501
  rb_define_method(LocalMemCache, "close", LocalMemCache__close, 0);
489
502
  rb_define_method(LocalMemCache, "size", LocalMemCache__size, 0);
503
+ rb_define_method(LocalMemCache, "check_consistency",
504
+ LocalMemCache__check_consistency, 0);
490
505
 
491
506
  lmc_rb_sym_namespace = ID2SYM(rb_intern("namespace"));
492
507
  lmc_rb_sym_filename = ID2SYM(rb_intern("filename"));
data/src/tests/bench.rb CHANGED
@@ -49,13 +49,3 @@ def measure_time(c, &block)
49
49
  end
50
50
 
51
51
  compare_speed(2_000_000)
52
- #test_gdbm(2_000_000)
53
-
54
- #$stdout.write "ht shm setting x 20000: "
55
- #tmeasure (2_000_000) {
56
- # v = $lm2.get("f").to_i + 1
57
- # #puts "v:#{v}"
58
- # $lm2.set("f", v)
59
- #}
60
- #puts "foo: #{$lm2.get("f")}"
61
-
data/src/tests/capi.c ADDED
@@ -0,0 +1,79 @@
1
+ #include <stdio.h>
2
+ #include <localmemcache.h>
3
+
4
+ #define fail_if(t,m) \
5
+ if ((t)) { printf("FAILED: %s\n", (m)); goto test_failed; } \
6
+ else { printf(" - %s\n", (m)); }
7
+
8
+ #define TEST_START(m) \
9
+ printf("%s\n", (m)); \
10
+ {
11
+
12
+ #define TEST_END \
13
+ }
14
+
15
+ int main() {
16
+ lmc_error_t e;
17
+ if (!local_memcache_drop_namespace("capitest", 0, 1, &e)) {
18
+ fprintf(stderr, "drop failed: %s\n", e.error_str);
19
+ return 1;
20
+ }
21
+ local_memcache_t *lmc = local_memcache_create("capitest", 0, 0, 0, &e);
22
+ if (!lmc) {
23
+ fprintf(stderr, "Couldn't create localmemcache: %s\n", e.error_str);
24
+ return 1;
25
+ }
26
+ if (!local_memcache_set(lmc, "foo", 3, "1", 1)) goto failed;
27
+
28
+ TEST_START("local_memcache_get_new")
29
+ size_t n_value;
30
+ char *value = local_memcache_get_new(lmc, "foo", 3, &n_value);
31
+ fail_if(!value, "value")
32
+ fail_if(n_value != 1, "n_value");
33
+ fail_if(value[0] != '1', "value[0]");
34
+ free(value);
35
+
36
+ char *value2 = local_memcache_get_new(lmc, "unknown", 7, &n_value);
37
+ fail_if(value2, "value2");
38
+ TEST_END
39
+
40
+ TEST_START("random_pair")
41
+ char *rk, *rv;
42
+ size_t n_rk, n_rv;
43
+ int r = local_memcache_random_pair_new(lmc, &rk, &n_rk, &rv, &n_rv);
44
+ fail_if(!r, "local_memcache_random_pair_new");
45
+ fail_if(n_rk != 3, "n_rk");
46
+ fail_if(rk[0] != 'f', "rk0");
47
+ fail_if(n_rv != 1, "n_rv");
48
+ fail_if(rv[0] != '1', "rv0");
49
+ free(rk);
50
+ free(rv);
51
+ TEST_END
52
+
53
+ if (!local_memcache_delete(lmc, "foo", 3)) goto failed;
54
+
55
+ TEST_START("random_pair(empty)")
56
+ char *rk, *rv;
57
+ size_t n_rk, n_rv;
58
+ int r = local_memcache_random_pair_new(lmc, &rk, &n_rk, &rv, &n_rv);
59
+ fail_if(!r, "local_memcache_random_pair_new:empty");
60
+ fail_if(rk, "rk:empty")
61
+ fail_if(rv, "rv:empty")
62
+ TEST_END
63
+
64
+ printf("Ok\n");
65
+
66
+ test_failed:
67
+ if (!local_memcache_free(lmc, &e)) {
68
+ fprintf(stderr, "Failed to release localmemcache: %s\n", e.error_str);
69
+ return 1;
70
+ }
71
+
72
+
73
+ return 0;
74
+
75
+ failed:
76
+ fprintf(stderr, "FAILED: %s\n", lmc->error.error_str);
77
+ return 1;
78
+
79
+ }
data/src/tests/capi.sh ADDED
@@ -0,0 +1,6 @@
1
+ #! /bin/sh
2
+
3
+ gcc -g -o capi.bin capi.c -I ../ -L ../ -llmc -lrt && \
4
+ valgrind -v --leak-check=full ./capi.bin 2>.tmp.valgrind
5
+
6
+ grep "\\.c" .tmp.valgrind
data/src/tests/crash CHANGED
@@ -5,7 +5,7 @@ script=$DIR/crash.rb
5
5
 
6
6
  export CFLAGS=-DDO_TEST_CRASH
7
7
  make -C .. clean && make -C .. && make -C ../ruby-binding &&
8
- ruby extconf.rb && make clean && make
8
+ ruby extconf.rb && make clean && make || exit 1
9
9
 
10
10
  #ulimit -c 0
11
11
 
@@ -0,0 +1,19 @@
1
+ #! /bin/sh
2
+ D=`dirname $0`
3
+ DIR=`cd $D; pwd`
4
+ script=$DIR/crash-small.rb
5
+
6
+ export CFLAGS=-DDO_TEST_CRASH
7
+ make -C .. clean && make -C .. && make -C ../ruby-binding &&
8
+ ruby extconf.rb && make clean && make || exit 1
9
+
10
+ #ulimit -c 0
11
+
12
+ if test "x$1" = "x-d"; then
13
+ irb -r $script
14
+ else
15
+ #valgrind --leak-check=full --tool=memcheck ruby $script
16
+ ruby $script
17
+ fi
18
+
19
+ #ulimit -c unlimited
@@ -0,0 +1,11 @@
1
+ $DIR=File.dirname(__FILE__)
2
+ ['.', '..', '../ruby-binding/'].each {|p| $:.unshift File.join($DIR, p) }
3
+
4
+ require 'localmemcache'
5
+
6
+ LocalMemCache.drop :namespace => "crash-small-t", :force => true
7
+ $lm = LocalMemCache.new :namespace=>"crash-small-t"
8
+
9
+ $lm["one"] = "1"
10
+ $lm.check_consistency
11
+
data/src/tests/crash.rb CHANGED
@@ -4,10 +4,6 @@ $DIR=File.dirname(__FILE__)
4
4
  require 'localmemcache'
5
5
 
6
6
  LocalMemCache.drop :namespace => "crash-t", :force => true
7
- #exit
8
- #puts "c"
9
- #LocalMemCache.check_namespace("crash-t");
10
-
11
7
 
12
8
  $pids = []
13
9
  5.times { $pids << fork {
@@ -23,7 +19,7 @@ $pids = []
23
19
  puts "#{$$} Worker finished"
24
20
  }}
25
21
 
26
- 40.times {
22
+ 10.times {
27
23
  $pid = fork {
28
24
  LocalMemCache.enable_test_crash
29
25
  $lm2 = LocalMemCache.new :namespace=>"crash-t"
data/src/tests/iter ADDED
@@ -0,0 +1,11 @@
1
+ #! /bin/sh
2
+ D=`dirname $0`
3
+ DIR=`cd $D; pwd`
4
+ script=$DIR/iter.rb
5
+
6
+ if test "x$1" = "x-d"; then
7
+ irb -r $script
8
+ else
9
+ #valgrind --leak-check=full --tool=memcheck ruby $script
10
+ ruby $script
11
+ fi
data/src/tests/iter.rb ADDED
@@ -0,0 +1,41 @@
1
+ $DIR=File.dirname(__FILE__)
2
+ ['.', '..', '../ruby-binding/'].each {|p| $:.unshift File.join($DIR, p) }
3
+
4
+ require 'bacon'
5
+ require 'localmemcache'
6
+
7
+ Bacon.summary_on_exit
8
+
9
+ LocalMemCache.drop :namespace => "iteration", :force => true
10
+ $l = LocalMemCache.new :namespace=>"iteration"
11
+
12
+ def compare_speed(n)
13
+
14
+ puts "lmc: filling dict"
15
+ measure_time(1) { n.times {|i| $l[i] = i } }
16
+ puts "lmc: iterating"
17
+ c = 0
18
+ measure_time(1) {
19
+ $l.each_pair {|k,v| c += 1 }
20
+ }
21
+ raise "lmc: error: c(#{c})!= n(#{n})" if c != n
22
+
23
+ puts "rhash: filling dict"
24
+ $hh = {}
25
+ measure_time(1) { n.times {|i| $hh[i] = i } }
26
+ puts "rhash: iterating"
27
+ c = 0
28
+ measure_time(1) {
29
+ $hh.each {|k,v| c += 1 }
30
+ }
31
+ raise "rhash: error: c!= n" if c != n
32
+ end
33
+
34
+ def measure_time(c, &block)
35
+ _then = Time.now
36
+ c.times { block.call }
37
+ now = Time.now
38
+ puts "#{(now - _then)*1000} ms"
39
+ end
40
+
41
+ compare_speed(400_000)
data/src/tests/lmc.rb CHANGED
@@ -53,6 +53,18 @@ describe 'LocalMemCache' do
53
53
  ll.random_pair.should.be.nil
54
54
  end
55
55
 
56
+ it 'should allow be consistent' do
57
+ $lm.check_consistency.should.be.true
58
+ end
59
+
60
+ it 'should support iteration' do
61
+ s = $lm.size
62
+ s.should.equal 3
63
+ c = 0
64
+ $lm.each_pair {|a,b| c += 1 }
65
+ s.should.equal c
66
+ end
67
+
56
68
  it 'should throw an exception when accessing a closed pool' do
57
69
  $lm.close
58
70
  should.raise(LocalMemCache::MemoryPoolClosed) { $lm.keys }
@@ -87,6 +99,7 @@ describe 'LocalMemCache' do
87
99
  end
88
100
 
89
101
 
102
+
90
103
  it 'should support checking of namespaces' do
91
104
  LocalMemCache.check :namespace => "test"
92
105
  end
@@ -11,5 +11,5 @@ if test "x$1" = "x-d"; then
11
11
  irb -r $script
12
12
  else
13
13
  #valgrind --leak-check=full --tool=memcheck ruby $script
14
- ruby $script
14
+ time ruby $script
15
15
  fi
@@ -0,0 +1,26 @@
1
+ #! /bin/sh
2
+
3
+
4
+ echo "SHM"
5
+ ./shm
6
+ echo "LMC"
7
+ ./lmc
8
+ echo "capi"
9
+ ./capi.sh
10
+
11
+ echo "bench"
12
+ ./bench
13
+ echo "bench-append"
14
+ ./bench-append
15
+
16
+ echo "ttlmc"
17
+ ./ttlmc
18
+ echo "ttrandom_pair"
19
+ ./ttrandom_pair
20
+ echo "ttalloc"
21
+ ./ttalloc
22
+
23
+ echo "parallelwrite"
24
+ ./parallelwrite
25
+ echo "crash"
26
+ ./crash
@@ -0,0 +1,7 @@
1
+ #! /bin/sh
2
+
3
+ ./configure && make -C src clean && make -C src && \
4
+ (cd src/ruby-binding; ruby extconf.rb) && \
5
+ make -C src/ruby-binding/ && \
6
+ (cd src/tests; ruby extconf.rb) && \
7
+ make -C src/tests/ && ./src/tests/lmc
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: localmemcache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sven C. Koehler
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-08-10 00:00:00 +00:00
12
+ date: 2009-10-02 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -131,15 +131,23 @@ files:
131
131
  - src/tests/bench.rb
132
132
  - src/tests/bench_keys
133
133
  - src/tests/bench_keys.rb
134
+ - src/tests/capi.c
135
+ - src/tests/capi.sh
134
136
  - src/tests/crash
137
+ - src/tests/crash-small
138
+ - src/tests/crash-small.rb
135
139
  - src/tests/crash.rb
136
140
  - src/tests/extconf.rb
141
+ - src/tests/iter
142
+ - src/tests/iter.rb
137
143
  - src/tests/lmc
138
144
  - src/tests/lmc.rb
139
145
  - src/tests/lmctestapi.c
140
146
  - src/tests/parallelwrite
141
147
  - src/tests/parallelwrite.rb
148
+ - src/tests/run-all-tests
142
149
  - src/tests/runtest.sh
150
+ - src/tests/sanity-test
143
151
  - src/tests/shm
144
152
  - src/tests/shm.rb
145
153
  - src/tests/torture.rb