localmemcache 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -13,5 +13,7 @@ config.log
13
13
  lmc_config.h
14
14
  Makefile
15
15
  *.so
16
+ *.bundle
17
+ *.log
16
18
  pkg
17
19
 
data/README CHANGED
@@ -19,6 +19,8 @@ SUPPORTED SYSTEMS
19
19
  - A CPU architecture with more than 32 bits is recommended, since otherwise you
20
20
  might run out of virtual address space when you use larger shared memory
21
21
  segments.
22
+ - OS X is not supported, because it doesn't have sem_timedwait() and
23
+ sem_getvalue().
22
24
 
23
25
  EXAMPLE
24
26
  =======
data/Rakefile CHANGED
@@ -24,6 +24,14 @@ task :changelog do
24
24
  }
25
25
  end
26
26
 
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 "
33
+ end
34
+
27
35
  task :c_api_package do
28
36
  tgz = "pkg/localmemcache-#{version}.tar.gz"
29
37
  sh "test -d pkg || mkdir pkg"
@@ -36,7 +44,7 @@ task :pushsite do
36
44
  sh "chmod 755 site"
37
45
  sh "chmod 644 site/*.html"
38
46
  sh "chmod 644 site/*.css"
39
- sh 'rsync --rsh="ssh -i $HOME/.ssh/id_rsa_projects -l sck" -avz site/ sck@localmemcache.rubyforge.org:/var/www/gforge-projects/localmemcache/'
47
+ sh 'rsync --rsh="ssh -i $HOME/.ssh/id_rsa_oss -l sck" -avz site/ sck@localmemcache.rubyforge.org:/var/www/gforge-projects/localmemcache/'
40
48
  end
41
49
 
42
50
  begin
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
data/configure CHANGED
@@ -600,6 +600,7 @@ PACKAGE_BUGREPORT=
600
600
 
601
601
  ac_unique_file="${srcdir}/src/lmc_config.h.in"
602
602
  ac_default_prefix=$prefix
603
+ ac_default_prefix=$prefix
603
604
  ac_subst_vars='SHELL
604
605
  PATH_SEPARATOR
605
606
  PACKAGE_NAME
@@ -2754,6 +2755,21 @@ fi
2754
2755
 
2755
2756
 
2756
2757
 
2758
+ { $as_echo "$as_me:$LINENO: checking for apple OS X" >&5
2759
+ $as_echo_n "checking for apple OS X... " >&6; }
2760
+ if test "x`uname`" = "xDarwin" && test "x$LMC_FORCE_BUILD" = "x"; then
2761
+ echo ""
2762
+ echo "-> Apple OS X is not supported because it doesn't have "
2763
+ echo " sem_getvalue and sem_timedwait!"
2764
+ echo " You may set the environment variable LMC_FORCE_BUILD "
2765
+ echo " to build a version of localmemcache that cannot recover "
2766
+ echo " from crashes."
2767
+ exit 2;
2768
+ fi
2769
+ { $as_echo "$as_me:$LINENO: result: Ok" >&5
2770
+ $as_echo "Ok" >&6; }
2771
+
2772
+
2757
2773
  { $as_echo "$as_me:$LINENO: checking version" >&5
2758
2774
  $as_echo_n "checking version... " >&6; }
2759
2775
  VERSION=`cat ${srcdir}/VERSION`
@@ -2785,11 +2801,32 @@ env > /tmp/lmc.env
2785
2801
  { $as_echo "$as_me:$LINENO: result: $PREFIX" >&5
2786
2802
  $as_echo "$PREFIX" >&6; }
2787
2803
 
2804
+ { $as_echo "$as_me:$LINENO: checking installation directory prefix" >&5
2805
+ $as_echo_n "checking installation directory prefix... " >&6; }
2806
+
2807
+ if test "$prefix" = "NONE"; then
2808
+ if test "x$PREFIX" != "x" ; then
2809
+ prefix=$PREFIX
2810
+ else
2811
+ prefix=/usr/local
2812
+ fi
2813
+ fi
2814
+
2815
+ PREFIX=$ac_default_prefix
2816
+ PREFIX=$prefix
2817
+ exec_prefix=$PREFIX
2818
+
2819
+ { $as_echo "$as_me:$LINENO: result: $PREFIX" >&5
2820
+ $as_echo "$PREFIX" >&6; }
2821
+
2822
+ { $as_echo "$as_me:$LINENO: checking if inside RubyGems" >&5
2823
+ $as_echo_n "checking if inside RubyGems... " >&6; }
2824
+ env > /tmp/lmc.env
2825
+ { $as_echo "$as_me:$LINENO: result: $PREFIX" >&5
2826
+ $as_echo "$PREFIX" >&6; }
2827
+
2788
2828
 
2789
2829
 
2790
- #--------------------------------------------------------------------
2791
- # Propagate prefix argument as installation directory.
2792
- #--------------------------------------------------------------------
2793
2830
 
2794
2831
 
2795
2832
 
@@ -3230,8 +3267,8 @@ _ACEOF
3230
3267
 
3231
3268
  cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
3232
3269
  # Files that config.status was made for.
3233
- config_files="`echo $ac_config_files`"
3234
- config_headers="`echo $ac_config_headers`"
3270
+ config_files="$ac_config_files"
3271
+ config_headers="$ac_config_headers"
3235
3272
 
3236
3273
  _ACEOF
3237
3274
 
@@ -4459,8 +4496,8 @@ _ACEOF
4459
4496
 
4460
4497
  cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
4461
4498
  # Files that config.status was made for.
4462
- config_files="`echo $ac_config_files`"
4463
- config_headers="`echo $ac_config_headers`"
4499
+ config_files="$ac_config_files"
4500
+ config_headers="$ac_config_headers"
4464
4501
 
4465
4502
  _ACEOF
4466
4503
 
@@ -5,6 +5,19 @@ AC_SUBST(CC)
5
5
  AC_PROG_RANLIB
6
6
  AC_SUBST(RANLIB)
7
7
 
8
+ AC_MSG_CHECKING([for apple OS X])
9
+ if test "x`uname`" = "xDarwin" && test "x$LMC_FORCE_BUILD" = "x"; then
10
+ echo ""
11
+ echo "-> Apple OS X is not supported because it doesn't have "
12
+ echo " sem_getvalue and sem_timedwait!"
13
+ echo " You may set the environment variable LMC_FORCE_BUILD "
14
+ echo " to build a version of localmemcache that cannot recover "
15
+ echo " from crashes."
16
+ exit 2;
17
+ fi
18
+ AC_MSG_RESULT([Ok])
19
+
20
+
8
21
  AC_MSG_CHECKING([version])
9
22
  VERSION=`cat ${srcdir}/VERSION`
10
23
  AC_SUBST(VERSION)
@@ -30,11 +43,28 @@ AC_MSG_CHECKING([if inside RubyGems])
30
43
  env > /tmp/lmc.env
31
44
  AC_MSG_RESULT([$PREFIX])
32
45
 
46
+ AC_MSG_CHECKING([installation directory prefix])
47
+
48
+ if test "$prefix" = "NONE"; then
49
+ if test "x$PREFIX" != "x" ; then
50
+ prefix=$PREFIX
51
+ else
52
+ prefix=/usr/local
53
+ fi
54
+ fi
55
+ AC_PREFIX_DEFAULT($prefix)
56
+ PREFIX=$ac_default_prefix
57
+ PREFIX=$prefix
58
+ exec_prefix=$PREFIX
59
+ AC_SUBST(PREFIX)
60
+ AC_MSG_RESULT([$PREFIX])
61
+
62
+ AC_MSG_CHECKING([if inside RubyGems])
63
+ env > /tmp/lmc.env
64
+ AC_MSG_RESULT([$PREFIX])
65
+
33
66
 
34
67
 
35
- #--------------------------------------------------------------------
36
- # Propagate prefix argument as installation directory.
37
- #--------------------------------------------------------------------
38
68
  AC_SUBST(PREFIX)
39
69
 
40
70
 
@@ -7,9 +7,44 @@
7
7
  <body>
8
8
 
9
9
  <div id="content">
10
- <h1>Efficiently sharing a hashtable between processes on a local Unix machine.</h1>
10
+ <h1>The beauty of memcached.&nbsp;&nbsp;For local data.&nbsp;&nbsp;Blazingly fast.</h1>
11
11
 
12
- <p><b>Localmemcache</b> aims to be faster than using <a href="http://www.danga.com/memcached/">memcached</a> locally by using shared memory and providing a similar interface for Ruby and C.
12
+ <p><b>Localmemcache</b> is a library for C and ruby that aims to provide
13
+ an interface similar to memcached but for accessing local data instead of
14
+ remote data. It's based on <b>mmap()</b>'ed shared memory for maximum speed.
15
+
16
+ <h2>Changes for 0.2.1</h2>
17
+
18
+ <li>Fixed a bug that prevented setting values in the hashtable</li>
19
+ <li>Accessing a closed memory cache does no longer result in a crash</li>
20
+ <li><b>Speed improvements</b>: On my machine localmemcache is now only
21
+ about <b>20</b>% slower than Ruby's hash (<b>0.2.0</b> was about
22
+ <b>40</b>% slower)</li>
23
+ <li><b>OS X is now officially not supported</b> as it lacks sem_timedwait and
24
+ sem_getvalue (You still can force a build but it won't be able to
25
+ recover from crashes.)</li>
26
+ <li>The environment variable <b>LMC_NAMESPACES_ROOT_PATH</b> can now be
27
+ used to override the default, which is <b>/var/tmp/localmemcache</b></li>
28
+
29
+
30
+ <h2>New features in 0.2.0</h2>
31
+
32
+ <li><b>Logging</b>: In case your application is terminated while
33
+ accessing the shared memory (eg by <b>kill -9</b>), it is now able to
34
+ <b>restore the integrity of your data</b>.</li>
35
+ <li><b>\0 character</b> can now be used in <b>keys</b> and
36
+ <b>values</b></li>
37
+ <li>The ruby binding now features a <b>keys()</b> method.</b>
38
+ <li>Added a <b>C API</b>. See <a href="http://github.com/sck/localmemcache/blob/8c753f74c53b107d271975bedcc1f91a3dbd6961/src/localmemcache.h">localmemcache.h</a></li>
39
+
40
+ <h2>Supported Systems</h2>
41
+
42
+ <li>Unix (for <b>mmap())</b></li>
43
+ <li>OS X is currently not supported because it doesn't have
44
+ sem_timedwait() and sem_getvalue()</li>
45
+ <li>A CPU architecture with more than <b>32 bits</b> is recommended, since
46
+ otherwise you might run out of virtual address space when you use larger
47
+ shared memory segments.</li>
13
48
 
14
49
  <h2>Install</h2>
15
50
 
@@ -18,6 +53,8 @@
18
53
  <pre><code>gem install localmemcache
19
54
  </code></pre>
20
55
 
56
+ If you just want to use the <b>C API</b>, download the .tar.gz from <a href="http://rubyforge.org/frs/?group_id=7925">here</a>.
57
+
21
58
  <h2>Using</h2>
22
59
  <p><pre><code>require 'localmemcache'
23
60
  $lm = LocalMemCache.new :namespace => :viewcounters
@@ -25,6 +62,7 @@ $lm[:foo] = 1
25
62
  $lm[:foo]
26
63
  $lm.delete(:foo)
27
64
  </code></pre>
65
+ (C version of this example: <a href="http://github.com/sck/localmemcache/blob/8c753f74c53b107d271975bedcc1f91a3dbd6961/example/hello.c">hello.c</a>)
28
66
  </p>
29
67
  <a href="http://github.com/sck/localmemcache/tree/master">Read more</a>.
30
68
 
@@ -32,7 +70,7 @@ $lm.delete(:foo)
32
70
 
33
71
  <p>Here's a quick speed comparison, made on an
34
72
  <b>Intel(R) Xeon(R) CPU E5205 @ 1.86GHz</b>:</p>
35
- Benchmark pseudo code:
73
+ Ruby benchmark pseudo code:
36
74
  <pre><code>2_000_000.times {
37
75
  index = rand(10000).to_s
38
76
  $hash.set(index, index)
@@ -41,13 +79,14 @@ Benchmark pseudo code:
41
79
  </code></pre>
42
80
 
43
81
  <pre>
44
- MemCache: <b>253,326.122</b> ms
45
- LocalMemCache: <b>6,055.552</b> ms
46
- Ruby's Hash: <b>4,963.313</b> ms
82
+ MemCache: <b>253,326.122</b> ms
83
+ LocalMemCache 0.2.1: <b>5,799.225</b> ms
84
+ Ruby's Hash: <b>4,963.313</b> ms
47
85
  </pre>
48
86
 
49
- <b>Localmemcache</b> is about <b>40</b> times faster than using memcached
50
- locally, and about <b>20%</b> slower than Ruby's hash.
87
+ So, on my machine <b>Localmemcache</b> 0.2.1 is about <b>43</b> times
88
+ faster than using memcached locally, and about <b>20</b>% slower than
89
+ Ruby's hash.
51
90
 
52
91
  <h2>Source code</h2>
53
92
 
@@ -57,6 +96,11 @@ can be retrieved by executing</p>
57
96
  <pre><code>git clone git://github.com/sck/localmemcache.git
58
97
  </code></pre>
59
98
 
99
+ <h2>Read on</h2>
100
+
101
+ A bit of documenation on how <b>localmemcache</b> <a
102
+ href="http://github.com/sck/localmemcache/blob/96b5e0863e1da7f17e249a6c8884984e0c23ee2f/INTERNALS">works</a>.
103
+
60
104
  <h2>License</h2>
61
105
 
62
106
  <p>Copyright (c) 2009 Sven C. Koehler (schween at s n a f u dot de)<br>
@@ -31,10 +31,10 @@ unsigned long ht_hash_key(const char *s, size_t l) {
31
31
  unsigned long v;
32
32
  size_t i;
33
33
  for (v = 0, i = 0; i++ < l; s++) { v = *s + 31 * v; }
34
- return v % HT_BUCKETS;
34
+ return v % LMC_HT_BUCKETS;
35
35
  }
36
36
 
37
- ht_hash_entry_t null_node = { 0, 0, 0 };
37
+ ht_hash_entry_t lmc_null_node = { 0, 0, 0 };
38
38
 
39
39
  va_ht_hash_t ht_hash_create(void *base, lmc_error_t *e) {
40
40
  va_ht_hash_t va_ht = lmc_valloc(base, sizeof(ht_hash_t));
@@ -54,7 +54,7 @@ int ht_hash_destroy(void *base, va_ht_hash_t ht) {
54
54
  ht_hash_entry_t *ht_lookup(void *base, va_ht_hash_t va_ht, const char *key,
55
55
  size_t n_key) {
56
56
  va_ht_hash_entry_t va_hr;
57
- ht_hash_entry_t *hr ;
57
+ ht_hash_entry_t *hr = &lmc_null_node;
58
58
  ht_hash_t *ht = base + va_ht;
59
59
  size_t i;
60
60
  for (va_hr = ht->va_buckets[ht_hash_key(key, n_key)];
@@ -73,7 +73,7 @@ ht_hash_entry_t *ht_lookup(void *base, va_ht_hash_t va_ht, const char *key,
73
73
  next:
74
74
  va_hr = hr->va_next;
75
75
  }
76
- return &null_node;
76
+ return &lmc_null_node;
77
77
  }
78
78
 
79
79
  ht_hash_entry_t *ht_lookup2(void *base, va_ht_hash_t va_ht, char *k) {
@@ -112,14 +112,12 @@ int ht_set(void *base, va_ht_hash_t va_ht, const char *key,
112
112
  ht_hash_entry_t *hr = ht_lookup(base, va_ht, key, n_key);
113
113
  unsigned v;
114
114
  if (hr->va_key == 0) {
115
- int free_key = 1;
116
115
  lmc_log_ht_set *l = (lmc_log_ht_set *)lmc_log_op(base, LMC_OP_HT_SET);
117
116
  if ((l->va_value = lmc_ht_strdup(base, value, n_value)) == 0 ||
118
117
  (l->va_key = lmc_ht_strdup(base, key, n_key)) == 0) {
119
118
  LMC_MEMORY_POOL_FULL("ht_set");
120
119
  goto failed;
121
120
  }
122
- free_key = 0;
123
121
  va_ht_hash_entry_t va = lmc_valloc(base, sizeof(ht_hash_entry_t));
124
122
  hr = va ? base + va : 0;
125
123
  if (hr == NULL) {
@@ -133,12 +131,6 @@ int ht_set(void *base, va_ht_hash_t va_ht, const char *key,
133
131
  hr->va_next = ht->va_buckets[v];
134
132
  ht->va_buckets[v] = va;
135
133
  hr->va_value = l->va_value;
136
- if (free_key) {
137
- size_t va = l->va_key;
138
- l->op_id = 0;
139
- LMC_TEST_CRASH
140
- lmc_free(base, va);
141
- }
142
134
  lmc_log_finish(base);
143
135
  } else {
144
136
  LMC_TEST_CRASH
@@ -159,7 +151,7 @@ failed_no_log:
159
151
 
160
152
  int ht_delete(void *base, va_ht_hash_t va_ht, const char *key, size_t n_key) {
161
153
  va_ht_hash_entry_t va_hr;
162
- ht_hash_entry_t *hr;
154
+ ht_hash_entry_t *hr = &lmc_null_node;
163
155
  size_t va_p = 0;
164
156
  ht_hash_t *ht = base + va_ht;
165
157
  size_t i;
@@ -190,12 +182,13 @@ int ht_delete(void *base, va_ht_hash_t va_ht, const char *key, size_t n_key) {
190
182
  return 0;
191
183
  }
192
184
 
193
- int ht_hash_iterate(void *base, va_ht_hash_t va_ht, void *ctx, ITERATOR_P(iter)) {
185
+ int ht_hash_iterate(void *base, va_ht_hash_t va_ht, void *ctx,
186
+ LMC_ITERATOR_P(iter)) {
194
187
  va_ht_hash_entry_t va_hr;
195
- ht_hash_entry_t *hr;
188
+ ht_hash_entry_t *hr = &lmc_null_node;
196
189
  ht_hash_t *ht = base + va_ht;
197
190
  size_t k;
198
- for (k = 0; k < HT_BUCKETS; k++) {
191
+ for (k = 0; k < LMC_HT_BUCKETS; k++) {
199
192
  for (va_hr = ht->va_buckets[k]; va_hr != 0 && hr != NULL;
200
193
  va_hr = hr->va_next) {
201
194
  hr = va_hr ? base + va_hr : 0;
@@ -209,19 +202,21 @@ int ht_check_memory(void *base, va_ht_hash_t va_ht) {
209
202
  char *bf = lmc_um_new_mem_usage_bitmap(base);
210
203
  if (!bf) return 0;
211
204
  va_ht_hash_entry_t va_hr;
212
- ht_hash_entry_t *hr;
205
+ ht_hash_entry_t *hr = &lmc_null_node;
213
206
  ht_hash_t *ht = base + va_ht;
214
207
  if (!lmc_um_mark_allocated(base, bf, va_ht)) goto failed;
215
208
  size_t k;
216
- for (k = 0; k < HT_BUCKETS; k++) {
209
+ for (k = 0; k < LMC_HT_BUCKETS; k++) {
217
210
  for (va_hr = ht->va_buckets[k]; va_hr != 0 && hr != NULL;
218
211
  va_hr = hr->va_next) {
219
212
  hr = va_hr ? base + va_hr : 0;
220
- if (!hr) continue;
213
+ if (!hr) goto next_bucket;
221
214
  if (!(lmc_um_mark_allocated(base, bf, va_hr) &&
222
215
  lmc_um_mark_allocated(base, bf, hr->va_key) &&
223
216
  lmc_um_mark_allocated(base, bf, hr->va_value))) goto failed;
224
217
  }
218
+ next_bucket:
219
+ continue;
225
220
  }
226
221
  lmc_um_find_leaks(base, bf);
227
222
  free(bf);
@@ -16,12 +16,13 @@ typedef struct {
16
16
  va_string_t va_value;
17
17
  } ht_hash_entry_t;
18
18
 
19
- #define HT_BUCKETS 499
20
- #define ITERATOR_P(n) int ((n)) (void *ctx, const char *key, const char *value)
19
+ #define LMC_HT_BUCKETS 499
20
+ #define LMC_ITERATOR_P(n) int ((n)) \
21
+ (void *ctx, const char *key, const char *value)
21
22
 
22
23
  typedef size_t va_ht_hash_t;
23
24
  typedef struct {
24
- va_ht_hash_entry_t va_buckets[HT_BUCKETS];
25
+ va_ht_hash_entry_t va_buckets[LMC_HT_BUCKETS];
25
26
  } ht_hash_t;
26
27
 
27
28
  va_ht_hash_t ht_hash_create(void *base, lmc_error_t *e);
@@ -33,7 +34,8 @@ const char *ht_get(void *base, va_ht_hash_t va_ht, const char *key, size_t n_key
33
34
  size_t *n_value);
34
35
  int ht_delete(void *base, va_ht_hash_t va_ht, const char *key, size_t n_key);
35
36
  int ht_hash_destroy(void *base, va_ht_hash_t ht);
36
- int ht_hash_iterate(void *base, va_ht_hash_t ht, void *ctx, ITERATOR_P(iter));
37
+ int ht_hash_iterate(void *base, va_ht_hash_t ht, void *ctx,
38
+ LMC_ITERATOR_P(iter));
37
39
 
38
40
  int ht_check_memory(void *base, va_ht_hash_t va_ht);
39
41
  int ht_redo(void *base, va_ht_hash_t va_ht, lmc_log_descriptor_t *l,
@@ -9,21 +9,13 @@
9
9
  #include <time.h>
10
10
  #include "lmc_lock.h"
11
11
 
12
- int c_l(lmc_lock_t *l, lmc_error_t *e) {
13
- if (!l) {
14
- lmc_handle_error_with_err_string("check_lock",
15
- "Semaphore not initialized", "LocalMemCacheError", e);
16
- }
17
- return l != NULL;
18
- }
19
-
20
12
  lmc_lock_t *lmc_lock_init(const char *namespace, int init, lmc_error_t *e) {
21
13
  lmc_lock_t *l = malloc(sizeof(lmc_lock_t));
22
14
  if (!l) return NULL;
23
15
  strncpy((char *)&l->namespace, namespace, 1023);
24
16
 
25
- lmc_handle_error((l->sem = sem_open(l->namespace, O_CREAT, 0600, init)) == NULL,
26
- "sem_open", "LockError", e);
17
+ lmc_handle_error((l->sem = sem_open(l->namespace, O_CREAT, 0600, init)) ==
18
+ NULL, "sem_open", "LockError", e);
27
19
  if (!l->sem) { free(l); return NULL; }
28
20
  return l;
29
21
  }
@@ -31,16 +23,12 @@ lmc_lock_t *lmc_lock_init(const char *namespace, int init, lmc_error_t *e) {
31
23
  int lmc_clear_namespace_lock(const char *namespace) {
32
24
  lmc_error_t e;
33
25
  lmc_lock_t *l = lmc_lock_init(namespace, 1, &e);
34
- //printf("clear_namespace locks: %s %d\n", namespace, lmc_lock_get_value(l));
35
26
  lmc_lock_repair(l);
36
- //printf("AFTER clear_namespace locks: %s %d\n", namespace,
37
- //lmc_lock_get_value(l));
38
27
  free(l);
39
28
  return 1;
40
29
  }
41
30
 
42
31
  int lmc_is_locked(lmc_lock_t* l, lmc_error_t *e) {
43
- if (!c_l(l, e)) { return 0; }
44
32
  if (sem_trywait(l->sem) == -1) {
45
33
  return 1;
46
34
  } else {
@@ -50,6 +38,9 @@ int lmc_is_locked(lmc_lock_t* l, lmc_error_t *e) {
50
38
  }
51
39
 
52
40
  int lmc_sem_timed_wait(lmc_lock_t* l) {
41
+ #ifdef __APPLE__
42
+ return sem_wait(l->sem);
43
+ #else
53
44
  struct timespec ts;
54
45
  clock_gettime(CLOCK_REALTIME, &ts);
55
46
  #ifdef DO_TEST_CRASH
@@ -58,17 +49,21 @@ int lmc_sem_timed_wait(lmc_lock_t* l) {
58
49
  ts.tv_sec += 2;
59
50
  #endif
60
51
  return sem_timedwait(l->sem, &ts);
52
+ #endif
61
53
  }
62
54
 
63
55
  int lmc_sem_timed_wait_mandatory(lmc_lock_t* l) {
56
+ #ifdef __APPLE__
57
+ return sem_wait(l->sem);
58
+ #else
64
59
  struct timespec ts;
65
60
  clock_gettime(CLOCK_REALTIME, &ts);
66
61
  ts.tv_sec += 20;
67
62
  return sem_timedwait(l->sem, &ts);
63
+ #endif
68
64
  }
69
65
 
70
66
  int lmc_is_lock_working(lmc_lock_t* l, lmc_error_t *e) {
71
- if (!c_l(l, e)) { return 0; }
72
67
  if (lmc_sem_timed_wait(l) == -1) {
73
68
  return 0;
74
69
  } else {
@@ -78,6 +73,9 @@ int lmc_is_lock_working(lmc_lock_t* l, lmc_error_t *e) {
78
73
  }
79
74
 
80
75
  void lmc_lock_repair(lmc_lock_t *l) {
76
+ #ifdef __APPLE__
77
+ return;
78
+ #else
81
79
  int v;
82
80
  sem_getvalue(l->sem, &v);
83
81
  if (v == 0) {
@@ -88,6 +86,7 @@ void lmc_lock_repair(lmc_lock_t *l) {
88
86
  sem_wait(l->sem);
89
87
  sem_getvalue(l->sem, &v);
90
88
  }
89
+ #endif
91
90
  }
92
91
 
93
92
  int lmc_lock_get_value(lmc_lock_t* l) {
@@ -97,7 +96,7 @@ int lmc_lock_get_value(lmc_lock_t* l) {
97
96
  }
98
97
 
99
98
  int lmc_lock_obtain(const char *where, lmc_lock_t* l, lmc_error_t *e) {
100
- if (!c_l(l,e)) { return 0; }
99
+ if (sem_trywait(l->sem) != -1) { return 1; }
101
100
  int r = lmc_sem_timed_wait(l);
102
101
  if (r == -1 && errno == ETIMEDOUT) {
103
102
  lmc_handle_error_with_err_string("sem_timedwait", strerror(errno),
@@ -114,10 +113,9 @@ int lmc_lock_obtain_mandatory(const char *where, lmc_lock_t* l, lmc_error_t *e)
114
113
  "LockTimedOut", e);
115
114
  return 0;
116
115
  }
117
- return c_l(l,e) && lmc_handle_error(r, "sem_wait", "LockError", e);
116
+ return lmc_handle_error(r, "sem_wait", "LockError", e);
118
117
  }
119
118
 
120
119
  int lmc_lock_release(const char *where, lmc_lock_t* l, lmc_error_t *e) {
121
- return c_l(l, e) && lmc_handle_error(sem_post(l->sem) == -1, "sem_post",
122
- "LockError", e);
120
+ return lmc_handle_error(sem_post(l->sem) == -1, "sem_post", "LockError", e);
123
121
  }
@@ -13,7 +13,11 @@
13
13
 
14
14
  #include "lmc_shm.h"
15
15
 
16
- #define LMC_SHM_ROOT_PATH "/var/tmp/localmemcache"
16
+ const char *lmc_namespace_root_path() {
17
+ const char *ep = getenv("LMC_NAMESPACES_ROOT_PATH");
18
+ if (ep) { return ep; }
19
+ return "/var/tmp/localmemcache";
20
+ }
17
21
 
18
22
  int lmc_does_file_exist(const char *fn) {
19
23
  struct stat st;
@@ -27,13 +31,13 @@ int lmc_file_size(const char *fn) {
27
31
  }
28
32
 
29
33
  void lmc_shm_ensure_root_path() {
30
- if (!lmc_does_file_exist(LMC_SHM_ROOT_PATH)) {
31
- mkdir(LMC_SHM_ROOT_PATH, 01777);
34
+ if (!lmc_does_file_exist(lmc_namespace_root_path())) {
35
+ mkdir(lmc_namespace_root_path(), 01777);
32
36
  }
33
37
  }
34
38
 
35
39
  void lmc_file_path_for_namespace(char *result, const char *ns) {
36
- snprintf(result, 1023, "%s/%s.lmc", LMC_SHM_ROOT_PATH, ns);
40
+ snprintf(result, 1023, "%s/%s.lmc", lmc_namespace_root_path(), ns);
37
41
  }
38
42
 
39
43
  int lmc_does_namespace_exist(const char *ns) {
@@ -69,7 +69,8 @@ lmc_mem_status_t lmc_status(void *base, char *where) {
69
69
  ms.total_mem = md->total_size;
70
70
  while (c) {
71
71
  if (!lmc_is_va_valid(base, (void *)c - base)) {
72
- printf("[%s] invalid pointer detected: %ld...\n", where, (void *)c - base);
72
+ printf("lmc: [%s] invalid pointer detected: %zd...\n", where,
73
+ (void *)c - base);
73
74
  lmc_dump(base);
74
75
  abort();
75
76
  }
@@ -105,7 +106,6 @@ int is_lmc_already_initialized(void *base) {
105
106
  void lmc_init_memory(void *ptr, size_t size) {
106
107
  lmc_mem_descriptor_t *md = ptr;
107
108
  size_t s = size - sizeof(lmc_mem_descriptor_t);
108
- // size: enough space for lmc_mem_descriptor_t + lmc_mem_chunk_descriptor_t
109
109
  md->first_free = sizeof(lmc_mem_descriptor_t);
110
110
  md->magic = 0xF00D;
111
111
  md->locked = 0;
@@ -122,10 +122,10 @@ size_t lmc_max(size_t a, size_t b) {
122
122
  size_t __s(char *where, lmc_mem_status_t ms, size_t mem_before, size_t expected_diff) {
123
123
  size_t free = ms.total_free_mem;
124
124
  printf("(%s) ", where);
125
- if (mem_before) { printf("[%ld:%zd] ", free - mem_before, expected_diff); }
125
+ if (mem_before) { printf("[%zd:%zd] ", free - mem_before, expected_diff); }
126
126
  printf("mem_free: %zu, chunks: %zu\n", free, ms.free_chunks);
127
127
  if (expected_diff && expected_diff != free - mem_before) {
128
- printf("expected_diff (%zu) != diff (%ld)\n", expected_diff,
128
+ printf("expected_diff (%zu) != diff (%zd)\n", expected_diff,
129
129
  free - mem_before);
130
130
  abort();
131
131
  }
@@ -134,10 +134,9 @@ size_t __s(char *where, lmc_mem_status_t ms, size_t mem_before, size_t expected_
134
134
 
135
135
  size_t lmc_valloc(void *base, size_t size) {
136
136
  lmc_mem_descriptor_t *md = base;
137
- // idea: MOD by power of 2
137
+ // consider: make size divisible by power of 2
138
138
  size_t s = lmc_max(size + sizeof(size_t),
139
139
  sizeof(lmc_mem_chunk_descriptor_t) + sizeof(size_t));
140
- // larger than available space?
141
140
  lmc_mem_chunk_descriptor_t *c = md_first_free(base);
142
141
  lmc_mem_chunk_descriptor_t *p = NULL;
143
142
  if (size == 0) { return 0; }
@@ -150,7 +149,6 @@ size_t lmc_valloc(void *base, size_t size) {
150
149
  c = base + c->next;
151
150
  }
152
151
  if (!c) {
153
- //fprintf(stderr, "lmc_valloc: Failed to allocate %d bytes!\n", size);
154
152
  return 0;
155
153
  }
156
154
  size_t r = 0;
@@ -176,8 +174,7 @@ size_t lmc_valloc(void *base, size_t size) {
176
174
  return r + sizeof(size_t);
177
175
  }
178
176
 
179
- // compact_chunks,
180
- void lmc_check_coalesce(void *base, size_t va_chunk) {
177
+ void lmc_compact_free_chunks(void *base, size_t va_chunk) {
181
178
  lmc_mem_descriptor_t *md = base;
182
179
  lmc_mem_chunk_descriptor_t *chunk = base + va_chunk;
183
180
  size_t c_size = chunk->size;
@@ -272,7 +269,7 @@ void __lmc_free(void *base, size_t va_used_chunk, size_t uc_size) {
272
269
  LMC_TEST_CRASH
273
270
  c_free_chunk->size += uc_size;
274
271
  LMC_TEST_CRASH
275
- lmc_check_coalesce(base, va_c_free_chunk);
272
+ lmc_compact_free_chunks(base, va_c_free_chunk);
276
273
  break;
277
274
  } else
278
275
  // ----------------------
@@ -286,7 +283,7 @@ void __lmc_free(void *base, size_t va_used_chunk, size_t uc_size) {
286
283
  mcd_used_chunk->next = c_free_chunk->next;
287
284
  mcd_used_chunk->size = uc_size + c_free_chunk->size;
288
285
  p->next = va_used_chunk;
289
- lmc_check_coalesce(base, va_used_chunk);
286
+ lmc_compact_free_chunks(base, va_used_chunk);
290
287
  break;
291
288
  }
292
289
  if (va_used_chunk >= va_c_free_chunk && va_used_chunk <= va_c_free_end) {
@@ -331,11 +328,6 @@ void lmc_free(void *base, size_t chunk) {
331
328
  __lmc_free(base, va_used_chunk, uc_size);
332
329
  }
333
330
 
334
- void lmc_realloc(void *base, size_t chunk) {
335
- // check if enough reserved space, true: resize; otherwise: alloc new and
336
- // then free
337
- }
338
-
339
331
  int lmc_um_getbit(char *bf, int i) {
340
332
  bf += i / 8; return (*bf & (1 << (i % 8))) != 0;
341
333
  }
@@ -349,7 +341,6 @@ void lmc_um_setbit(char *bf, int i, int v) {
349
341
  int lmc_um_find_leaks(void *base, char *bf) {
350
342
  lmc_mem_descriptor_t *md = base;
351
343
  size_t i;
352
- // check if gap size if smaller than sizeof(free_chunk_t)
353
344
  int gap = 0;
354
345
  size_t gs = 0;
355
346
  size_t m;
@@ -382,7 +373,6 @@ int lmc_um_find_leaks(void *base, char *bf) {
382
373
  space += i - gs;
383
374
  __lmc_free(base, gs, i - gs);
384
375
  }
385
- //printf("total leaks: %zd block, %zd bytes total\n", gap_count, space);
386
376
  return 1;
387
377
  }
388
378
 
@@ -396,17 +386,7 @@ int lmc_um_check_unmarked(void *base, char *bf, size_t va, size_t size) {
396
386
  while (*b == n && i < end - sizeof(size_t)) {
397
387
  i += sizeof(size_t) * 8; b++;
398
388
  }
399
- if (lmc_um_getbit(bf, i) != 0) {
400
- //printf("umarked2: FAILED at: %zd\n", i);
401
- //printf("umarked2: FAILED start: %zd\n", va);
402
- //size_t d = i;
403
- //while (lmc_um_getbit(bf, d) != 0) { --d; }
404
- //printf("area starts at: %zd\n", d);
405
- //size_t e = i;
406
- //while (lmc_um_getbit(bf, e) != 0) { ++e; }
407
- //printf("area ends at: %zd\n", e);
408
- return 0;
409
- }
389
+ if (lmc_um_getbit(bf, i) != 0) { return 0; }
410
390
  }
411
391
  return 1;
412
392
  }
@@ -416,7 +396,7 @@ int lmc_um_mark(void *base, char *bf, size_t va, size_t size) {
416
396
  lmc_mem_descriptor_t *md = base;
417
397
  if ((va > sizeof(lmc_mem_descriptor_t)) &&
418
398
  (!lmc_is_va_valid(base, va) || !lmc_is_va_valid(base, va + size))) {
419
- printf("Error: VA start out of range: va: %zd - %zd max %zd!\n",
399
+ printf("lmc: Error: VA start out of range: va: %zd - %zd max %zd!\n",
420
400
  va, va + size, md->total_size);
421
401
  return 0;
422
402
  }
@@ -284,7 +284,8 @@ int local_memcache_free(local_memcache_t *lmc, lmc_error_t *e) {
284
284
  return r;
285
285
  }
286
286
 
287
- int local_memcache_iterate(local_memcache_t *lmc, void *ctx, ITERATOR_P(iter)) {
287
+ int local_memcache_iterate(local_memcache_t *lmc, void *ctx,
288
+ LMC_ITERATOR_P(iter)) {
288
289
  if (!lmc_lock_shm_region("local_memcache_iterate", lmc)) return 0;
289
290
  int r = ht_hash_iterate(lmc->base, lmc->va_hash, ctx, iter);
290
291
  if (!lmc_unlock_shm_region("local_memcache_iterate", lmc)) return 0;
@@ -34,7 +34,8 @@ int local_memcache_set(local_memcache_t *lmc, const char *key, size_t n_key,
34
34
  const char* value, size_t n_value);
35
35
  int local_memcache_delete(local_memcache_t *lmc, char *key, size_t n_key);
36
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));
37
+ int local_memcache_iterate(local_memcache_t *lmc, void *ctx,
38
+ LMC_ITERATOR_P(iter));
38
39
  int local_memcache_clear_namespace(const char *namespace, int repair,
39
40
  lmc_error_t *e);
40
41
  int local_memcache_check_namespace(const char *namespace, lmc_error_t *e);
@@ -8,7 +8,11 @@ $srcs = ['rblocalmemcache.c']
8
8
  $objs = ['rblocalmemcache.o']
9
9
 
10
10
  $CFLAGS << " -g -I .."
11
- $LDFLAGS << " ../liblmc.a -lpthread -lrt "
11
+ $LDFLAGS << " -lpthread "
12
+ $LOCAL_LIBS << "../liblmc.a"
13
+ if have_library("rt")
14
+ $LDFLAGS << " -lrt"
15
+ end
12
16
 
13
17
  dir_config('rblocalmemcache')
14
18
  create_makefile('rblocalmemcache')
@@ -18,6 +18,7 @@ class LocalMemCache
18
18
  class RecoveryFailed < LocalMemCacheError; end
19
19
  class ShmLockFailed < LocalMemCacheError; end
20
20
  class ShmUnlockFailed < LocalMemCacheError; end
21
+ class MemoryPoolClosed < LocalMemCacheError; end
21
22
 
22
23
  # Creates a new handle for accessing a shared memory region.
23
24
  #
@@ -5,6 +5,14 @@
5
5
  #include <ruby.h>
6
6
  #include "localmemcache.h"
7
7
 
8
+ #ifndef RSTRING_LEN
9
+ #define RSTRING_LEN(x) RSTRING(x)->len
10
+ #endif
11
+
12
+ #ifndef RSTRING_PTR
13
+ #define RSTRING_PTR(x) RSTRING(x)->ptr
14
+ #endif
15
+
8
16
  /* :nodoc: */
9
17
  long long_value(VALUE i) { return NUM2LONG(rb_Integer(i)); }
10
18
  /* :nodoc: */
@@ -17,6 +25,7 @@ char *rstring_ptr(VALUE s) {
17
25
  return r ? r : "nil";
18
26
  }
19
27
 
28
+ /* :nodoc: */
20
29
  size_t rstring_length(VALUE s) {
21
30
  size_t r = NIL_P(s) ? 0 : RSTRING_LEN(rb_String(s));
22
31
  return r;
@@ -26,39 +35,78 @@ static VALUE ruby_string(const char *s) { return s ? rb_str_new2(s) : Qnil; }
26
35
  /* :nodoc: */
27
36
  int bool_value(VALUE v) { return v == Qtrue; }
28
37
 
38
+ /* :nodoc: */
29
39
  static VALUE lmc_ruby_string2(const char *s, size_t l) {
30
40
  return s ? rb_str_new(s, l) : Qnil;
31
41
  }
32
42
 
43
+ /* :nodoc: */
33
44
  static VALUE lmc_ruby_string(const char *s) {
34
45
  return lmc_ruby_string2(s + sizeof(size_t), *(size_t *) s);
35
46
  }
36
47
 
48
+ typedef struct {
49
+ local_memcache_t *lmc;
50
+ int open;
51
+ } rb_lmc_handle_t;
37
52
 
38
53
  static VALUE LocalMemCache;
39
54
 
40
55
  /* :nodoc: */
41
- void raise_exception(lmc_error_t *e) {
42
- VALUE eid = rb_intern(e->error_type);
56
+ void __rb_lmc_raise_exception(const char *error_type, const char *m) {
57
+ VALUE eid = rb_intern(error_type);
43
58
  VALUE k = rb_const_get(LocalMemCache, eid);
44
- rb_raise(k, e->error_str);
59
+ rb_raise(k, m);
60
+ }
61
+
62
+ /* :nodoc: */
63
+ void rb_lmc_raise_exception(lmc_error_t *e) {
64
+ __rb_lmc_raise_exception(e->error_type, e->error_str);
65
+ }
66
+
67
+ /* :nodoc: */
68
+ local_memcache_t *rb_lmc_check_handle_access(rb_lmc_handle_t *h) {
69
+ if (!h || (h->open == 0) || !h->lmc) {
70
+ __rb_lmc_raise_exception("MemoryPoolClosed", "Pool is closed");
71
+ return 0;
72
+ }
73
+ return h->lmc;
74
+ }
75
+
76
+ /* :nodoc: */
77
+ static void rb_lmc_free_handle(rb_lmc_handle_t *h) {
78
+ lmc_error_t e;
79
+ local_memcache_free(rb_lmc_check_handle_access(h), &e);
45
80
  }
46
81
 
82
+
47
83
  /* :nodoc: */
48
84
  static VALUE LocalMemCache__new2(VALUE klass, VALUE namespace, VALUE size_mb) {
49
85
  lmc_error_t e;
50
- local_memcache_t *lmc = local_memcache_create(rstring_ptr(namespace),
86
+ local_memcache_t *l = local_memcache_create(rstring_ptr(namespace),
51
87
  double_value(size_mb), &e);
52
- if (!lmc) { raise_exception(&e); }
53
- return Data_Wrap_Struct(klass, NULL, local_memcache_free, lmc);
88
+ if (!l) rb_lmc_raise_exception(&e);
89
+ rb_lmc_handle_t *h = calloc(1, sizeof(rb_lmc_handle_t));
90
+ if (!h) rb_raise(rb_eRuntimeError, "memory allocation error");
91
+ h->lmc = l;
92
+ h->open = 1;
93
+ return Data_Wrap_Struct(klass, NULL, rb_lmc_free_handle, h);
54
94
  }
55
95
 
96
+ /* :nodoc: */
97
+ local_memcache_t *get_LocalMemCache(VALUE obj) {
98
+ rb_lmc_handle_t *h;
99
+ Data_Get_Struct(obj, rb_lmc_handle_t, h);
100
+ return rb_lmc_check_handle_access(h);
101
+ }
102
+
103
+
56
104
  /* :nodoc: */
57
105
  static VALUE LocalMemCache__clear_namespace(VALUE klass, VALUE ns,
58
106
  VALUE repair) {
59
107
  lmc_error_t e;
60
108
  if (!local_memcache_clear_namespace(rstring_ptr(ns), bool_value(repair), &e)) {
61
- raise_exception(&e);
109
+ rb_lmc_raise_exception(&e);
62
110
  }
63
111
  return Qnil;
64
112
  }
@@ -67,7 +115,7 @@ static VALUE LocalMemCache__clear_namespace(VALUE klass, VALUE ns,
67
115
  static VALUE LocalMemCache__check_namespace(VALUE klass, VALUE ns) {
68
116
  lmc_error_t e;
69
117
  if (!local_memcache_check_namespace(rstring_ptr(ns), &e)) {
70
- raise_exception(&e);
118
+ rb_lmc_raise_exception(&e);
71
119
  }
72
120
  return Qnil;
73
121
  }
@@ -85,12 +133,6 @@ static VALUE LocalMemCache__disable_test_crash(VALUE klass) {
85
133
  return Qnil;
86
134
  }
87
135
 
88
- /* :nodoc: */
89
- local_memcache_t *get_LocalMemCache(VALUE obj) {
90
- local_memcache_t *lmc;
91
- Data_Get_Struct(obj, local_memcache_t, lmc);
92
- return lmc;
93
- }
94
136
 
95
137
  /*
96
138
  * call-seq:
@@ -120,7 +162,7 @@ static VALUE LocalMemCache__set(VALUE obj, VALUE key, VALUE value) {
120
162
  local_memcache_t *lmc = get_LocalMemCache(obj);
121
163
  if (!local_memcache_set(lmc, rstring_ptr(key), rstring_length(key),
122
164
  rstring_ptr(value), rstring_length(value))) {
123
- raise_exception(&lmc->error);
165
+ rb_lmc_raise_exception(&lmc->error);
124
166
  }
125
167
  return Qnil;
126
168
  }
@@ -145,7 +187,11 @@ static VALUE LocalMemCache__delete(VALUE obj, VALUE key) {
145
187
  */
146
188
  static VALUE LocalMemCache__close(VALUE obj) {
147
189
  lmc_error_t e;
148
- if (!local_memcache_free(get_LocalMemCache(obj), &e)) raise_exception(&e);
190
+ rb_lmc_handle_t *h;
191
+ Data_Get_Struct(obj, rb_lmc_handle_t, h);
192
+ if (!local_memcache_free(rb_lmc_check_handle_access(h), &e))
193
+ rb_lmc_raise_exception(&e);
194
+ h->open = 0;
149
195
  return Qnil;
150
196
  }
151
197
 
@@ -153,12 +199,14 @@ typedef struct {
153
199
  VALUE ary;
154
200
  } lmc_ruby_iter_collect_keys;
155
201
 
202
+ /* :nodoc: */
156
203
  int lmc_ruby_iter(void *ctx, const char* key, const char* value) {
157
204
  lmc_ruby_iter_collect_keys *data = ctx;
158
205
  rb_ary_push(data->ary, lmc_ruby_string(key));
159
206
  return 1;
160
207
  }
161
208
 
209
+ /* :nodoc: */
162
210
  static VALUE __LocalMemCache__keys(VALUE d) {
163
211
  VALUE obj = rb_ary_entry(d, 0);
164
212
  VALUE r = rb_ary_entry(d, 1);
@@ -7,8 +7,12 @@ $defs << "-DRUBY_VERSION_CODE=#{RUBY_VERSION.gsub(/\D/, '')}"
7
7
  $srcs = ['lmctestapi.c']
8
8
  $objs = ['lmctestapi.o']
9
9
 
10
- $CFLAGS << " -D_REENTRANT -g -I .."
11
- $LDFLAGS << " ../liblmc.a -lpthread -lrt"
10
+ $CFLAGS << " -g -I .."
11
+ $LDFLAGS << " -lpthread "
12
+ $LOCAL_LIBS << "../liblmc.a"
13
+ if have_library("rt")
14
+ $LDFLAGS << " -lrt"
15
+ end
12
16
 
13
17
  dir_config('lmctestapi')
14
18
  create_makefile('lmctestapi')
@@ -6,11 +6,11 @@ require 'localmemcache'
6
6
 
7
7
  Bacon.summary_on_exit
8
8
 
9
- LocalMemCache.clear_namespace("testing", true)
10
- $lm = LocalMemCache.new :namespace=>"testing"
9
+ LocalMemCache.clear_namespace("test", true)
10
+ $lm = LocalMemCache.new :namespace=>"test"
11
11
 
12
- LocalMemCache.clear_namespace("testing-small", true)
13
- $lms = LocalMemCache.new :namespace=>"testing-small", :size_mb => 0.01;
12
+ LocalMemCache.clear_namespace("test-small", true)
13
+ $lms = LocalMemCache.new :namespace=>"test-small", :size_mb => 0.01;
14
14
 
15
15
  describe 'LocalMemCache' do
16
16
 
@@ -42,17 +42,22 @@ describe 'LocalMemCache' do
42
42
  $lm["null"].should.equal "foo\0goo"
43
43
  end
44
44
 
45
+ it 'should throw an exception when accessing a closed pool' do
46
+ $lm.close
47
+ should.raise(LocalMemCache::MemoryPoolClosed) { $lm.keys }
48
+ end
49
+
45
50
  it 'should throw exception if pool is full' do
46
51
  $lms["one"] = "a";
47
52
  should.raise(LocalMemCache::MemoryPoolFull) { $lms["two"] = "b" * 8000; }
48
53
  end
49
54
 
50
55
  it 'should support checking of namespaces' do
51
- LocalMemCache.check_namespace("testing")
56
+ LocalMemCache.check_namespace("test")
52
57
  end
53
58
 
54
59
  it 'should support clearing of namespaces' do
55
- LocalMemCache.clear_namespace("testing")
60
+ LocalMemCache.clear_namespace("test")
56
61
  end
57
62
 
58
63
 
@@ -7,6 +7,14 @@
7
7
  #include "lmc_hashtable.h"
8
8
  #include "lmc_valloc.h"
9
9
 
10
+ #ifndef RSTRING_LEN
11
+ #define RSTRING_LEN(x) RSTRING(x)->len
12
+ #endif
13
+
14
+ #ifndef RSTRING_PTR
15
+ #define RSTRING_PTR(x) RSTRING(x)->ptr
16
+ #endif
17
+
10
18
  void *memp = NULL;
11
19
  static VALUE OutOfMemoryError;
12
20
 
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.2.0
4
+ version: 0.2.1
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-03-30 00:00:00 +00:00
12
+ date: 2009-04-05 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies: []
15
15