localmemcache 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/README +18 -11
  2. data/Rakefile +22 -7
  3. data/VERSION +1 -1
  4. data/bench/common.rb +7 -0
  5. data/bench/gdbm_vs_lmc +10 -0
  6. data/bench/gdbm_vs_lmc.rb +29 -0
  7. data/bench/lmc_bench +11 -0
  8. data/bench/lmc_bench.rb +27 -0
  9. data/bench/memcached_bench +8 -0
  10. data/bench/memcached_bench.rb +19 -0
  11. data/bench/tyrant_bench +10 -0
  12. data/bench/tyrant_bench.rb +19 -0
  13. data/example/hello.c +1 -1
  14. data/site/doc/classes/LocalMemCache/ArgError.html +113 -0
  15. data/site/doc/classes/LocalMemCache/InitError.html +113 -0
  16. data/site/doc/classes/LocalMemCache/LocalMemCacheError.html +111 -0
  17. data/site/doc/classes/LocalMemCache/LockError.html +113 -0
  18. data/site/doc/classes/LocalMemCache/LockTimedOut.html +113 -0
  19. data/site/doc/classes/LocalMemCache/MemoryPoolClosed.html +113 -0
  20. data/site/doc/classes/LocalMemCache/MemoryPoolFull.html +113 -0
  21. data/site/doc/classes/LocalMemCache/OutOfMemoryError.html +113 -0
  22. data/site/doc/classes/LocalMemCache/RecoveryFailed.html +113 -0
  23. data/site/doc/classes/LocalMemCache/ShmError.html +113 -0
  24. data/site/doc/classes/LocalMemCache/ShmLockFailed.html +113 -0
  25. data/site/doc/classes/LocalMemCache/ShmUnlockFailed.html +113 -0
  26. data/site/doc/classes/LocalMemCache.html +515 -0
  27. data/site/doc/classes/LocalMemCache.src/M000001.html +19 -0
  28. data/site/doc/classes/LocalMemCache.src/M000002.html +18 -0
  29. data/site/doc/classes/LocalMemCache.src/M000003.html +18 -0
  30. data/site/doc/classes/LocalMemCache.src/M000004.html +39 -0
  31. data/site/doc/classes/LocalMemCache.src/M000005.html +29 -0
  32. data/site/doc/classes/LocalMemCache.src/M000006.html +23 -0
  33. data/site/doc/classes/LocalMemCache.src/M000007.html +23 -0
  34. data/site/doc/classes/LocalMemCache.src/M000008.html +22 -0
  35. data/site/doc/classes/LocalMemCache.src/M000009.html +24 -0
  36. data/site/doc/classes/LocalMemCache.src/M000010.html +24 -0
  37. data/site/doc/classes/LocalMemCache.src/M000011.html +22 -0
  38. data/site/doc/classes/LocalMemCache.src/M000012.html +22 -0
  39. data/site/doc/created.rid +1 -0
  40. data/site/doc/files/__/src/ruby-binding/extconf_rb.html +108 -0
  41. data/site/doc/files/__/src/ruby-binding/localmemcache_rb.html +108 -0
  42. data/site/doc/files/__/src/ruby-binding/rblocalmemcache_c.html +101 -0
  43. data/site/doc/fr_class_index.html +39 -0
  44. data/site/doc/fr_file_index.html +28 -0
  45. data/site/doc/fr_method_index.html +38 -0
  46. data/site/doc/index.html +24 -0
  47. data/site/doc/rdoc-style.css +208 -0
  48. data/site/index.html +50 -46
  49. data/src/lmc_common.c +22 -0
  50. data/src/lmc_common.h +4 -0
  51. data/src/lmc_hashtable.h +1 -1
  52. data/src/lmc_lock.c +17 -3
  53. data/src/lmc_shm.c +4 -2
  54. data/src/lmc_valloc.c +6 -5
  55. data/src/lmc_valloc.h +1 -0
  56. data/src/localmemcache.c +56 -35
  57. data/src/localmemcache.h +161 -4
  58. data/src/ruby-binding/localmemcache.rb +48 -16
  59. data/src/ruby-binding/rblocalmemcache.c +131 -24
  60. data/src/tests/bench.rb +1 -1
  61. data/src/tests/lmc.rb +11 -2
  62. metadata +48 -7
  63. data/INTERNALS +0 -26
  64. data/example/hello.bin +0 -0
data/src/localmemcache.c CHANGED
@@ -33,30 +33,35 @@ int lmc_release_lock_flag(void *base, lmc_error_t *e) {
33
33
  return 1;
34
34
  }
35
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 = '-';
36
+ int lmc_namespace_or_filename(char *result, const char* ons, const char *ofn,
37
+ lmc_error_t *e) {
38
+ if (ons) {
39
+ lmc_clean_string(result, ons);
40
+ return 1;
41
+ }
42
+ if (ofn) {
43
+ size_t n = strlen(ofn);
44
+ if (n > 1010) { n = 1010; }
45
+ char *d = result;
46
+ if (!lmc_is_filename(ofn)) {
47
+ strcpy(d, "./");
48
+ d += 2;
49
49
  }
50
+ strcpy(d, ofn);
51
+ return 1;
50
52
  }
51
- *d = 0x0;
53
+ lmc_handle_error_with_err_string("lmc_namespace_or_filename",
54
+ "Need to supply either namespace or filename argument", "ArgError", e);
55
+ return 0;
52
56
  }
53
57
 
54
- int local_memcache_clear_namespace(const char *namespace, int repair,
55
- lmc_error_t *e) {
58
+ int local_memcache_clear_namespace(const char *namespace, const char *filename,
59
+ int repair, lmc_error_t *e) {
56
60
  char clean_ns[1024];
57
- lmc_clean_namespace_string((char *)clean_ns, namespace);
61
+ if (!lmc_namespace_or_filename((char *)clean_ns, namespace, filename, e))
62
+ return 1;
58
63
  lmc_clean_namespace((char *)clean_ns, e);
59
- if (repair) {
64
+ if (repair) {
60
65
  lmc_lock_t *l = lmc_lock_init((char *)clean_ns, 1, e);
61
66
  lmc_lock_repair(l);
62
67
  free(l);
@@ -70,6 +75,8 @@ int local_memcache_clear_namespace(const char *namespace, int repair,
70
75
  return 1;
71
76
  }
72
77
 
78
+ int __local_memcache_check_namespace(const char *clean_ns, lmc_error_t *e);
79
+
73
80
  local_memcache_t *__local_memcache_create(const char *namespace, size_t size,
74
81
  int force, int *ok, lmc_error_t* e) {
75
82
  int d;
@@ -89,11 +96,11 @@ retry:
89
96
  }
90
97
  if (!lmc_is_lock_working(lmc->lock, e)) {
91
98
  if (!force) {
92
- if (local_memcache_check_namespace(namespace, e)) goto retry;
99
+ if (__local_memcache_check_namespace(namespace, e)) goto retry;
93
100
  lmc_handle_error_with_err_string("local_memcache_create",
94
101
  "Failed to repair shared memory!", "ShmLockFailed", e);
95
102
  goto failed;
96
- }
103
+ }
97
104
  *ok = 0;
98
105
  }
99
106
  {
@@ -133,26 +140,26 @@ failed:
133
140
  return NULL;
134
141
  }
135
142
 
136
- local_memcache_t *local_memcache_create(const char *namespace, double size_mb,
137
- lmc_error_t* e) {
143
+ local_memcache_t *local_memcache_create(const char *namespace,
144
+ const char *filename, double size_mb, lmc_error_t* e) {
138
145
  char clean_ns[1024];
139
146
  double s = size_mb == 0.0 ? 1024.0 : size_mb;
140
147
  size_t si = s * 1024 * 1024;
141
148
  //printf("size: %f, s: %f, si: %zd\n", size_mb, s, si);
142
- lmc_clean_namespace_string((char *)clean_ns, namespace);
149
+ if (!lmc_namespace_or_filename((char *)clean_ns, namespace, filename, e))
150
+ return 0;
143
151
  return __local_memcache_create((char *)clean_ns, si, 0, 0, e);
144
152
  }
145
153
 
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);
154
+ int __local_memcache_check_namespace(const char *clean_ns, lmc_error_t *e) {
149
155
  char check_lock_name[1024];
150
156
  snprintf((char *)check_lock_name, 1023, "%s-check", (char *)clean_ns);
151
157
 
152
158
  if (!lmc_does_namespace_exist((char *)clean_ns)) {
153
159
  lmc_clear_namespace_lock(check_lock_name);
154
- lmc_clear_namespace_lock(namespace);
155
- printf("namespace '%s' does not exist!\n", (char *)clean_ns);
160
+ lmc_clear_namespace_lock(clean_ns);
161
+ fprintf(stderr, "[localmemcache] namespace '%s' does not exist!\n",
162
+ (char *)clean_ns);
156
163
  return 1;
157
164
  }
158
165
 
@@ -170,12 +177,14 @@ int local_memcache_check_namespace(const char *namespace, lmc_error_t *e) {
170
177
  local_memcache_t *lmc = __local_memcache_create((char *)clean_ns, ns_size,
171
178
  1, &ok, e);
172
179
  if (!lmc) {
173
- printf("WOAH: lmc == 0!\n");
180
+ lmc_handle_error_with_err_string("__local_memcache_create",
181
+ "Unable to attach memory pool", "InitError", e);
174
182
  goto failed;
175
183
  }
176
184
  md = lmc->base;
177
185
  if (!ok) {
178
- printf("[lmc] Auto repairing namespace '%s'\n", namespace);
186
+ fprintf(stderr, "[localmemcache] Auto repairing namespace '%s'\n",
187
+ clean_ns);
179
188
  if (!md->locked) goto release;
180
189
  if (md->log.op_id == 0) goto unlock_and_release;
181
190
  if (ht_redo(lmc->base, md->va_hash, &md->log, e)) goto unlock_and_release;
@@ -208,24 +217,36 @@ failed:
208
217
  lmc_lock_release("local_memcache_check_namespace", check_l, e);
209
218
  check_lock_failed:
210
219
  free(check_l);
211
- printf("[lmc] Failed to repair namespace '%s'\n", namespace);
220
+ fprintf(stderr, "[localmemcache] Failed to repair namespace '%s'\n",
221
+ clean_ns);
212
222
  return 0;
213
223
  }
214
224
 
225
+ int local_memcache_check_namespace(const char *namespace, const char *filename,
226
+ lmc_error_t *e) {
227
+ char clean_ns[1024];
228
+ if (!lmc_namespace_or_filename((char *)clean_ns, namespace, filename, e))
229
+ return 0;
230
+ return __local_memcache_check_namespace(clean_ns, e);
231
+ }
232
+
233
+
215
234
 
216
235
  int lmc_lock_shm_region(const char *who, local_memcache_t *lmc) {
217
236
  int r;
218
237
  int retry_counter = 0;
219
238
  retry:
220
239
  if (retry_counter++ > 10) {
221
- printf("[lmc] Too many retries: Cannot repair namespace '%s'\n",
222
- lmc->namespace);
240
+ fprintf(stderr, "[localmemcache] Too many retries: "
241
+ "Cannot repair namespace '%s'\n", lmc->namespace);
223
242
  return 0;
224
243
  }
225
244
  r = lmc_lock_obtain(who, lmc->lock, &lmc->error);
226
245
  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);
246
+ if (__local_memcache_check_namespace(lmc->namespace,
247
+ &lmc->error)) goto retry;
248
+ fprintf(stderr, "[localmemcache] Cannot repair namespace '%s'\n",
249
+ lmc->namespace);
229
250
  }
230
251
  if (!r) return 0;
231
252
  if (!lmc_set_lock_flag(lmc->base, &lmc->error)) {
data/src/localmemcache.h CHANGED
@@ -15,6 +15,70 @@
15
15
  #define LOCAL_MEMCACHE_FAILED 0
16
16
  #define LOCAL_MEMCACHE_SUCCESS 1
17
17
 
18
+ /*
19
+ * LocalMemCache provides for a Hashtable of strings in shared memory (via a
20
+ * memory mapped file), which thus can be shared between processes on a
21
+ * computer. Here is an example of its usage:
22
+ *
23
+ * #include <stdio.h>
24
+ * #include <localmemcache.h>
25
+ *
26
+ * int main() {
27
+ * lmc_error_t e;
28
+ * // To use a filename instead of a namespace:
29
+ * // lmc = local_memcache_create(0, "filename.lmc", 0, &e);
30
+ * local_memcache_t *lmc = local_memcache_create("viewcounters", 0, 0, &e);
31
+ * if (!lmc) {
32
+ * fprintf(stderr, "Couldn't create localmemcache: %s\n", e.error_str);
33
+ * return 1;
34
+ * }
35
+ * if (!local_memcache_set(lmc, "foo", 3, "1", 1)) goto failed;
36
+ * size_t n_value;
37
+ * char *value = local_memcache_get_new(lmc, "foo", 3, &n_value);
38
+ * if (!value) goto failed;
39
+ * free(value);
40
+ * if (!local_memcache_delete(lmc, "foo", 3)) goto failed;
41
+ * if (!local_memcache_free(lmc, &e)) {
42
+ * fprintf(stderr, "Failed to release localmemcache: %s\n", e.error_str);
43
+ * return 1;
44
+ * }
45
+ *
46
+ * return 0;
47
+ *
48
+ * failed:
49
+ * fprintf(stderr, "%s\n", lmc->error.error_str);
50
+ * return 1;
51
+ *
52
+ * }
53
+ *
54
+ * == Default sizes of memory pools
55
+ *
56
+ * The default size for memory pools is 1024 (MB). It cannot be changed later,
57
+ * so choose a size that will provide enough space for all your data. You
58
+ * might consider setting this size to the maximum filesize of your
59
+ * filesystem. Also note that while these memory pools may look large on your
60
+ * disk, they really aren't, because with sparse files only those parts of the
61
+ * file which contain non-null data actually use disk space.
62
+ *
63
+ * == Automatic recovery from crashes
64
+ *
65
+ * In case a process is terminated while accessing a memory pool, other
66
+ * processes will wait for the lock up to 2 seconds, and will then try to
67
+ * resume the aborted operation. This can also be done explicitly by using
68
+ * LocalMemCache.check(options).
69
+ *
70
+ * == Clearing memory pools
71
+ *
72
+ * Removing memory pools can be done with LocalMemCache.clear(options).
73
+ *
74
+ * == Environment
75
+ *
76
+ * If you use the :namespace parameter, the .lmc file for your namespace will
77
+ * reside in /var/tmp/localmemcache. This can be overriden by setting the
78
+ * LMC_NAMESPACES_ROOT_PATH variable in the environment.
79
+ *
80
+ */
81
+
18
82
  typedef struct {
19
83
  char *namespace;
20
84
  size_t size;
@@ -26,22 +90,115 @@ typedef struct {
26
90
  lmc_error_t error;
27
91
  } local_memcache_t;
28
92
 
29
- local_memcache_t *local_memcache_create(const char *namespace, double size_mb,
30
- lmc_error_t *e);
93
+ /*
94
+ * Creates a new handle for accessing a shared memory region.
95
+ *
96
+ * lmc_error_t e;
97
+ * // open via namespace
98
+ * local_memcache_t *lmc = local_memcache_create("viewcounters", 0, 0, &e);
99
+ * // open via filename
100
+ * local_memcache_t *lmc = local_memcache_create(0, "./foo.lmc", 0, &e);
101
+ *
102
+ * You must supply at least a namespace or filename parameter
103
+ *
104
+ * The size_mb defaults to 1024 (1 GB).
105
+ *
106
+ * If you use the namespace parameter, the .lmc file for your namespace will
107
+ * reside in /var/tmp/localmemcache. This can be overriden by setting the
108
+ * LMC_NAMESPACES_ROOT_PATH variable in the environment.
109
+ *
110
+ * When you first call .new for a previously not existing memory pool, a
111
+ * sparse file will be created and memory and disk space will be allocated to
112
+ * hold the empty hashtable (about 100K), so the size_mb refers
113
+ * only to the maximum size of the memory pool. .new for an already existing
114
+ * memory pool will only map the already previously allocated RAM into the
115
+ * virtual address space of your process.
116
+ */
117
+ local_memcache_t *local_memcache_create(const char *namespace,
118
+ const char *filename, double size_mb, lmc_error_t* e);
119
+
120
+ /*
121
+ * Retrieve string value from hashtable.
122
+ *
123
+ * It will return a newly allocated string which you need to free() after use.
124
+ */
31
125
  char *local_memcache_get_new(local_memcache_t *lmc, const char *key,
32
126
  size_t n_key, size_t *n_value);
127
+
128
+ /*
129
+ * Set string value in hashtable.
130
+ */
33
131
  int local_memcache_set(local_memcache_t *lmc, const char *key, size_t n_key,
34
132
  const char* value, size_t n_value);
133
+
134
+ /*
135
+ * Deletes key from hashtable.
136
+ */
35
137
  int local_memcache_delete(local_memcache_t *lmc, char *key, size_t n_key);
138
+
139
+ /*
140
+ * Releases memory pool handle.
141
+ */
36
142
  int local_memcache_free(local_memcache_t *lmc, lmc_error_t *e);
143
+
144
+ /*
145
+ * Iterate over key value pairs in memory pool
146
+ *
147
+ * example:
148
+ * typedef struct {
149
+ * ...
150
+ * } collector_t;
151
+ *
152
+ * int my_collect(void *ctx, const char* key, const char* value) {
153
+ * collector_t *c = ctx;
154
+ * ....
155
+ * }
156
+ *
157
+ * local_memcache_t *lmc;
158
+ * collector_t c;
159
+ * local_memcache_iterate(lmc, (void *) &c, my_collect);
160
+ *
161
+ * The memory pool will be locked while iteration takes place, so try to make
162
+ * sure you can iterate within under 2 seconds otherwise other waiting
163
+ * processes will try to remove the lock (2 seconds is the timeout for
164
+ * triggering the automatic recovery.)
165
+ *
166
+ */
37
167
  int local_memcache_iterate(local_memcache_t *lmc, void *ctx,
38
168
  LMC_ITERATOR_P(iter));
39
- int local_memcache_clear_namespace(const char *namespace, int repair,
169
+
170
+ /*
171
+ * Deletes a memory pool. If repair is 1, locked semaphores are
172
+ * removed as well.
173
+ *
174
+ * WARNING: Do only call this method with the repair option if you are sure
175
+ * that you really want to remove this memory pool and no more processes are
176
+ * still using it.
177
+ *
178
+ * If you delete a pool and other processes still have handles open on it, the
179
+ * status of these handles becomes undefined. There's no way for a process to
180
+ * know when a handle is not valid anymore, so only delete a memory pool if
181
+ * you are sure that all handles are closed.
182
+ *
183
+ * The memory pool must be specified by either setting the filename or
184
+ * namespace parameter.
185
+ */
186
+ int local_memcache_clear_namespace(const char *namespace, const char *filename,
187
+ int repair, lmc_error_t *e);
188
+ /*
189
+ * Tries to repair a corrupt namespace. Usually one doesn't call this method
190
+ * directly, it's invoked automatically when operations time out.
191
+ *
192
+ * The memory pool must be specified by either setting the filename or
193
+ * namespace parameter.
194
+ */
195
+ int local_memcache_check_namespace(const char *namespace, const char *filename,
40
196
  lmc_error_t *e);
41
- int local_memcache_check_namespace(const char *namespace, lmc_error_t *e);
42
197
 
198
+ /* internal, do not use */
43
199
  const char *__local_memcache_get(local_memcache_t *lmc,
44
200
  const char *key, size_t n_key, size_t *n_value);
45
201
 
202
+ /* internal, do not use */
46
203
  int lmc_unlock_shm_region(const char *who, local_memcache_t *lmc);
47
204
  #endif
@@ -1,12 +1,5 @@
1
1
  require 'rblocalmemcache'
2
2
 
3
- # == Overview
4
- # TestRDocUsage: A useless file
5
- #
6
- # == Example
7
- #
8
- # Usage: ruby testRDocUsage.rb [options]
9
- #
10
3
  class LocalMemCache
11
4
 
12
5
  class LocalMemCacheError < StandardError; end
@@ -15,29 +8,68 @@ class LocalMemCache
15
8
  class LockError < LocalMemCacheError; end
16
9
  class LockTimedOut < LocalMemCacheError; end
17
10
  class OutOfMemoryError < LocalMemCacheError; end
11
+ class ArgError < LocalMemCacheError; end
12
+ class InitError < LocalMemCacheError; end
18
13
  class RecoveryFailed < LocalMemCacheError; end
19
14
  class ShmLockFailed < LocalMemCacheError; end
20
15
  class ShmUnlockFailed < LocalMemCacheError; end
21
16
  class MemoryPoolClosed < LocalMemCacheError; end
22
17
 
23
18
  # Creates a new handle for accessing a shared memory region.
24
- #
19
+ #
25
20
  # LocalMemCache.new :namespace=>"foo", :size_mb=> 1
26
- #
27
- # The namespace parameter is mandatory.
21
+ #
22
+ # LocalMemCache.new :filename=>"./foo.lmc"
23
+ #
24
+ # You must supply at least a :namespace or :filename parameter
28
25
  # The size_mb defaults to 1024 (1 GB).
29
26
  #
27
+ # If you use the :namespace parameter, the .lmc file for your namespace will
28
+ # reside in /var/tmp/localmemcache. This can be overriden by setting the
29
+ # LMC_NAMESPACES_ROOT_PATH variable in the environment.
30
+ #
31
+ # When you first call .new for a previously not existing memory pool, a
32
+ # sparse file will be created and memory and disk space will be allocated to
33
+ # hold the empty hashtable (about 100K), so the size_mb refers
34
+ # only to the maximum size of the memory pool. .new for an already existing
35
+ # memory pool will only map the already previously allocated RAM into the
36
+ # virtual address space of your process.
37
+ #
38
+ #
30
39
  def self.new(options)
31
40
  o = { :size_mb => 0 }.update(options || {})
32
- raise "Missing mandatory option ':namespace'" if !o[:namespace]
33
- _new(o[:namespace].to_s, o[:size_mb].to_f);
41
+ _new(o);
34
42
  end
35
43
 
36
- # Deletes the given namespaces, removing semaphores if necessary.
37
- # Do only use if you are sure the namespace is not used anymore by other
38
- # processes.
44
+ # NOTE: This method is deprecated, use LocalMemCache.clear(*args) instead.
45
+ #
46
+ # Deletes a memory pool. If the repair flag is set, locked semaphores are
47
+ # removed as well.
39
48
  #
49
+ # If you delete a pool and other processes still have handles open on it, the
50
+ # status of these handles becomes undefined. There's no way for a process to
51
+ # know when a handle is not valid anymore, so only delete a memory pool if
52
+ # you are sure that all handles are closed.
53
+ #
54
+ # WARNING: Do only call this method with the repair=true flag if you are sure
55
+ # that you really want to remove this memory pool and no more processes are
56
+ # still using it.
40
57
  def self.clear_namespace(namespace, repair = false)
41
- _clear_namespace(namespace.to_s, repair)
58
+ clear :namespace => namespace.to_s, :repair => repair
59
+ end
60
+
61
+ # NOTE: This method is deprecated, use LocalMemCache.check(*args) instead.
62
+ #
63
+ # Tries to repair a corrupt namespace. Usually one doesn't call this method
64
+ # directly, it's invoked automatically when operations time out.
65
+ #
66
+ # valid options are
67
+ # [:namespace]
68
+ # [:filename]
69
+ #
70
+ # The memory pool must be specified by either setting the :filename or
71
+ # :namespace option. The default for :repair is false.
72
+ def self.check_namespace(namespace)
73
+ check :namespace => namespace.to_s
42
74
  end
43
75
  end
@@ -15,7 +15,6 @@
15
15
  #define RSTRING_PTR(x) RSTRING(x)->ptr
16
16
  #endif
17
17
 
18
-
19
18
  #if RUBY_VERSION_CODE >= 190
20
19
  #define ruby_errinfo rb_errinfo()
21
20
  #endif
@@ -32,6 +31,11 @@ char *rstring_ptr(VALUE s) {
32
31
  return r ? r : "nil";
33
32
  }
34
33
 
34
+ char *rstring_ptr_null(VALUE s) {
35
+ char* r = NIL_P(s) ? NULL : RSTRING_PTR(rb_String(s));
36
+ return r ? r : NULL;
37
+ }
38
+
35
39
  /* :nodoc: */
36
40
  size_t rstring_length(VALUE s) {
37
41
  size_t r = NIL_P(s) ? 0 : RSTRING_LEN(rb_String(s));
@@ -58,6 +62,10 @@ typedef struct {
58
62
  } rb_lmc_handle_t;
59
63
 
60
64
  static VALUE LocalMemCache;
65
+ static VALUE lmc_rb_sym_namespace;
66
+ static VALUE lmc_rb_sym_filename;
67
+ static VALUE lmc_rb_sym_size_mb;
68
+ static VALUE lmc_rb_sym_repair;
61
69
 
62
70
  /* :nodoc: */
63
71
  void __rb_lmc_raise_exception(const char *error_type, const char *m) {
@@ -86,12 +94,20 @@ static void rb_lmc_free_handle(rb_lmc_handle_t *h) {
86
94
  local_memcache_free(rb_lmc_check_handle_access(h), &e);
87
95
  }
88
96
 
97
+ void lmc_check_dict(VALUE o) {
98
+ if (TYPE(o) != T_HASH) {
99
+ rb_raise(rb_eArgError, "expected a Hash");
100
+ }
101
+ }
89
102
 
90
103
  /* :nodoc: */
91
- static VALUE LocalMemCache__new2(VALUE klass, VALUE namespace, VALUE size_mb) {
104
+ static VALUE LocalMemCache__new2(VALUE klass, VALUE o) {
105
+ lmc_check_dict(o);
92
106
  lmc_error_t e;
93
- local_memcache_t *l = local_memcache_create(rstring_ptr(namespace),
94
- double_value(size_mb), &e);
107
+ local_memcache_t *l = local_memcache_create(
108
+ rstring_ptr_null(rb_hash_aref(o, lmc_rb_sym_namespace)),
109
+ rstring_ptr_null(rb_hash_aref(o, lmc_rb_sym_filename)),
110
+ double_value(rb_hash_aref(o, lmc_rb_sym_size_mb)), &e);
95
111
  if (!l) rb_lmc_raise_exception(&e);
96
112
  rb_lmc_handle_t *h = calloc(1, sizeof(rb_lmc_handle_t));
97
113
  if (!h) rb_raise(rb_eRuntimeError, "memory allocation error");
@@ -107,21 +123,61 @@ local_memcache_t *get_LocalMemCache(VALUE obj) {
107
123
  return rb_lmc_check_handle_access(h);
108
124
  }
109
125
 
110
-
111
- /* :nodoc: */
112
- static VALUE LocalMemCache__clear_namespace(VALUE klass, VALUE ns,
113
- VALUE repair) {
126
+ /*
127
+ * call-seq: LocalMemCache.clear(*args)
128
+ *
129
+ * Deletes a memory pool. If the :repair option is set, locked semaphores are
130
+ * removed as well.
131
+ *
132
+ * WARNING: Do only call this method with the :repair option if you are sure
133
+ * that you really want to remove this memory pool and no more processes are
134
+ * still using it.
135
+ *
136
+ * If you delete a pool and other processes still have handles open on it, the
137
+ * status of these handles becomes undefined. There's no way for a process to
138
+ * know when a handle is not valid anymore, so only delete a memory pool if
139
+ * you are sure that all handles are closed.
140
+ *
141
+ * valid options for clear are
142
+ * [:namespace]
143
+ * [:filename]
144
+ * [:repair]
145
+ *
146
+ * The memory pool must be specified by either setting the :filename or
147
+ * :namespace option. The default for :repair is false.
148
+ */
149
+ static VALUE LocalMemCache__clear(VALUE klass, VALUE o) {
150
+ lmc_check_dict(o);
114
151
  lmc_error_t e;
115
- if (!local_memcache_clear_namespace(rstring_ptr(ns), bool_value(repair), &e)) {
152
+ if (!local_memcache_clear_namespace(
153
+ rstring_ptr_null(rb_hash_aref(o, lmc_rb_sym_namespace)),
154
+ rstring_ptr_null(rb_hash_aref(o, lmc_rb_sym_filename)),
155
+ bool_value(rb_hash_aref(o, lmc_rb_sym_repair)), &e)) {
116
156
  rb_lmc_raise_exception(&e);
117
157
  }
118
158
  return Qnil;
119
159
  }
120
160
 
121
- /* :nodoc: */
122
- static VALUE LocalMemCache__check_namespace(VALUE klass, VALUE ns) {
161
+ /*
162
+ * call-seq: LocalMemCache.check(*args)
163
+ *
164
+ * Tries to repair a corrupt namespace. Usually one doesn't call this method
165
+ * directly, it's invoked automatically when operations time out.
166
+ *
167
+ * valid options are
168
+ * [:namespace]
169
+ * [:filename]
170
+ *
171
+ * The memory pool must be specified by either setting the :filename or
172
+ * :namespace option.
173
+ */
174
+ static VALUE LocalMemCache__check(VALUE klass, VALUE o) {
175
+ lmc_check_dict(o);
123
176
  lmc_error_t e;
124
- if (!local_memcache_check_namespace(rstring_ptr(ns), &e)) {
177
+ if (!local_memcache_check_namespace(
178
+ rstring_ptr_null(rb_hash_aref(o, lmc_rb_sym_namespace)),
179
+ rstring_ptr_null(rb_hash_aref(o, lmc_rb_sym_filename)),
180
+ &e)) {
125
181
  rb_lmc_raise_exception(&e);
126
182
  }
127
183
  return Qnil;
@@ -140,13 +196,12 @@ static VALUE LocalMemCache__disable_test_crash(VALUE klass) {
140
196
  return Qnil;
141
197
  }
142
198
 
143
-
144
199
  /*
145
200
  * call-seq:
146
- * lmc.get(key) -> Qnil
147
- * lmc[key] -> Qnil
201
+ * lmc.get(key) -> string value or nil
202
+ * lmc[key] -> string value or nil
148
203
  *
149
- * Retrieve value from hashtable.
204
+ * Retrieve string value from hashtable.
150
205
  */
151
206
  static VALUE LocalMemCache__get(VALUE obj, VALUE key) {
152
207
  size_t l;
@@ -162,9 +217,9 @@ static VALUE LocalMemCache__get(VALUE obj, VALUE key) {
162
217
  * lmc.set(key, value) -> Qnil
163
218
  * lmc[key]=value -> Qnil
164
219
  *
165
- * Set value for key in hashtable.
220
+ * Set value for key in hashtable. Value and key will be converted to
221
+ * string.
166
222
  */
167
-
168
223
  static VALUE LocalMemCache__set(VALUE obj, VALUE key, VALUE value) {
169
224
  local_memcache_t *lmc = get_LocalMemCache(obj);
170
225
  if (!local_memcache_set(lmc, rstring_ptr(key), rstring_length(key),
@@ -178,7 +233,7 @@ static VALUE LocalMemCache__set(VALUE obj, VALUE key, VALUE value) {
178
233
  * call-seq:
179
234
  * lmc.delete(key) -> Qnil
180
235
  *
181
- * Deletes key from hashtable.
236
+ * Deletes key from hashtable. The key is converted to string.
182
237
  */
183
238
  static VALUE LocalMemCache__delete(VALUE obj, VALUE key) {
184
239
  return local_memcache_delete(get_LocalMemCache(obj),
@@ -244,13 +299,60 @@ static VALUE LocalMemCache__keys(VALUE obj) {
244
299
  return rb_ary_entry(d, 1);
245
300
  }
246
301
 
302
+ /*
303
+ * Document-class: LocalMemCache
304
+ *
305
+ * <code>LocalMemCache</code> provides for a Hashtable of strings in shared
306
+ * memory (via a memory mapped file), which thus can be shared between
307
+ * processes on a computer. Here is an example of its usage:
308
+ *
309
+ * $lm = LocalMemCache.new :namespace => "viewcounters"
310
+ * $lm[:foo] = 1
311
+ * $lm[:foo] # -> "1"
312
+ * $lm.delete(:foo)
313
+ *
314
+ * <code>LocalMemCache</code> can also be used as a persistent key value
315
+ * database, just use the :filename instead of the :namespace parameter.
316
+ *
317
+ * $lm = LocalMemCache.new :filename => "my-database.lmc"
318
+ * $lm[:foo] = 1
319
+ * $lm[:foo] # -> "1"
320
+ * $lm.delete(:foo)
321
+ *
322
+ * == Default sizes of memory pools
323
+ *
324
+ * The default size for memory pools is 1024 (MB). It cannot be changed later,
325
+ * so choose a size that will provide enough space for all your data. You
326
+ * might consider setting this size to the maximum filesize of your
327
+ * filesystem. Also note that while these memory pools may look large on your
328
+ * disk, they really aren't, because with sparse files only those parts of the
329
+ * file which contain non-null data actually use disk space.
330
+ *
331
+ * == Automatic recovery from crashes
332
+ *
333
+ * In case a process is terminated while accessing a memory pool, other
334
+ * processes will wait for the lock up to 2 seconds, and will then try to
335
+ * resume the aborted operation. This can also be done explicitly by using
336
+ * LocalMemCache.check(options).
337
+ *
338
+ * == Clearing memory pools
339
+ *
340
+ * Removing memory pools can be done with LocalMemCache.clear(options).
341
+ *
342
+ * == Environment
343
+ *
344
+ * If you use the :namespace parameter, the .lmc file for your namespace will
345
+ * reside in /var/tmp/localmemcache. This can be overriden by setting the
346
+ * LMC_NAMESPACES_ROOT_PATH variable in the environment.
347
+ *
348
+ */
247
349
  void Init_rblocalmemcache() {
248
350
  LocalMemCache = rb_define_class("LocalMemCache", rb_cObject);
249
- rb_define_singleton_method(LocalMemCache, "_new", LocalMemCache__new2, 2);
250
- rb_define_singleton_method(LocalMemCache, "_clear_namespace",
251
- LocalMemCache__clear_namespace, 2);
252
- rb_define_singleton_method(LocalMemCache, "check_namespace",
253
- LocalMemCache__check_namespace, 1);
351
+ rb_define_singleton_method(LocalMemCache, "_new", LocalMemCache__new2, 1);
352
+ rb_define_singleton_method(LocalMemCache, "clear",
353
+ LocalMemCache__clear, 1);
354
+ rb_define_singleton_method(LocalMemCache, "check",
355
+ LocalMemCache__check, 1);
254
356
  rb_define_singleton_method(LocalMemCache, "disable_test_crash",
255
357
  LocalMemCache__disable_test_crash, 0);
256
358
  rb_define_singleton_method(LocalMemCache, "enable_test_crash",
@@ -262,4 +364,9 @@ void Init_rblocalmemcache() {
262
364
  rb_define_method(LocalMemCache, "[]=", LocalMemCache__set, 2);
263
365
  rb_define_method(LocalMemCache, "keys", LocalMemCache__keys, 0);
264
366
  rb_define_method(LocalMemCache, "close", LocalMemCache__close, 0);
367
+
368
+ lmc_rb_sym_namespace = ID2SYM(rb_intern("namespace"));
369
+ lmc_rb_sym_filename = ID2SYM(rb_intern("filename"));
370
+ lmc_rb_sym_size_mb = ID2SYM(rb_intern("size_mb"));
371
+ lmc_rb_sym_repair = ID2SYM(rb_intern("repair"));
265
372
  }