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 +2 -0
- data/README +2 -0
- data/Rakefile +9 -1
- data/VERSION +1 -1
- data/configure +44 -7
- data/configure.in +33 -3
- data/site/index.html +52 -8
- data/src/lmc_hashtable.c +14 -19
- data/src/lmc_hashtable.h +6 -4
- data/src/lmc_lock.c +17 -19
- data/src/lmc_shm.c +8 -4
- data/src/lmc_valloc.c +10 -30
- data/src/localmemcache.c +2 -1
- data/src/localmemcache.h +2 -1
- data/src/ruby-binding/extconf.rb +5 -1
- data/src/ruby-binding/localmemcache.rb +1 -0
- data/src/ruby-binding/rblocalmemcache.c +64 -16
- data/src/tests/extconf.rb +6 -2
- data/src/tests/lmc.rb +11 -6
- data/src/tests/lmctestapi.c +8 -0
- metadata +2 -2
data/.gitignore
CHANGED
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/
|
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.
|
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="
|
3234
|
-
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="
|
4463
|
-
config_headers="
|
4499
|
+
config_files="$ac_config_files"
|
4500
|
+
config_headers="$ac_config_headers"
|
4464
4501
|
|
4465
4502
|
_ACEOF
|
4466
4503
|
|
data/configure.in
CHANGED
@@ -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
|
|
data/site/index.html
CHANGED
@@ -7,9 +7,44 @@
|
|
7
7
|
<body>
|
8
8
|
|
9
9
|
<div id="content">
|
10
|
-
<h1>
|
10
|
+
<h1>The beauty of memcached. For local data. Blazingly fast.</h1>
|
11
11
|
|
12
|
-
<p><b>Localmemcache</b>
|
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
|
-
|
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:
|
45
|
-
LocalMemCache: <b>
|
46
|
-
Ruby's Hash:
|
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>
|
50
|
-
locally, and about <b>20
|
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>
|
data/src/lmc_hashtable.c
CHANGED
@@ -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 %
|
34
|
+
return v % LMC_HT_BUCKETS;
|
35
35
|
}
|
36
36
|
|
37
|
-
ht_hash_entry_t
|
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 &
|
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,
|
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 <
|
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 <
|
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)
|
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);
|
data/src/lmc_hashtable.h
CHANGED
@@ -16,12 +16,13 @@ typedef struct {
|
|
16
16
|
va_string_t va_value;
|
17
17
|
} ht_hash_entry_t;
|
18
18
|
|
19
|
-
#define
|
20
|
-
#define
|
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[
|
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,
|
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,
|
data/src/lmc_lock.c
CHANGED
@@ -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)) ==
|
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 (
|
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
|
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
|
122
|
-
"LockError", e);
|
120
|
+
return lmc_handle_error(sem_post(l->sem) == -1, "sem_post", "LockError", e);
|
123
121
|
}
|
data/src/lmc_shm.c
CHANGED
@@ -13,7 +13,11 @@
|
|
13
13
|
|
14
14
|
#include "lmc_shm.h"
|
15
15
|
|
16
|
-
|
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(
|
31
|
-
mkdir(
|
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",
|
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) {
|
data/src/lmc_valloc.c
CHANGED
@@ -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: %
|
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("[%
|
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 (%
|
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
|
-
//
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
}
|
data/src/localmemcache.c
CHANGED
@@ -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,
|
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;
|
data/src/localmemcache.h
CHANGED
@@ -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,
|
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);
|
data/src/ruby-binding/extconf.rb
CHANGED
@@ -8,7 +8,11 @@ $srcs = ['rblocalmemcache.c']
|
|
8
8
|
$objs = ['rblocalmemcache.o']
|
9
9
|
|
10
10
|
$CFLAGS << " -g -I .."
|
11
|
-
$LDFLAGS << "
|
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
|
42
|
-
VALUE eid = rb_intern(
|
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,
|
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 *
|
86
|
+
local_memcache_t *l = local_memcache_create(rstring_ptr(namespace),
|
51
87
|
double_value(size_mb), &e);
|
52
|
-
if (!
|
53
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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);
|
data/src/tests/extconf.rb
CHANGED
@@ -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 << " -
|
11
|
-
$LDFLAGS << "
|
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')
|
data/src/tests/lmc.rb
CHANGED
@@ -6,11 +6,11 @@ require 'localmemcache'
|
|
6
6
|
|
7
7
|
Bacon.summary_on_exit
|
8
8
|
|
9
|
-
LocalMemCache.clear_namespace("
|
10
|
-
$lm = LocalMemCache.new :namespace=>"
|
9
|
+
LocalMemCache.clear_namespace("test", true)
|
10
|
+
$lm = LocalMemCache.new :namespace=>"test"
|
11
11
|
|
12
|
-
LocalMemCache.clear_namespace("
|
13
|
-
$lms = LocalMemCache.new :namespace=>"
|
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("
|
56
|
+
LocalMemCache.check_namespace("test")
|
52
57
|
end
|
53
58
|
|
54
59
|
it 'should support clearing of namespaces' do
|
55
|
-
LocalMemCache.clear_namespace("
|
60
|
+
LocalMemCache.clear_namespace("test")
|
56
61
|
end
|
57
62
|
|
58
63
|
|
data/src/tests/lmctestapi.c
CHANGED
@@ -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.
|
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-
|
12
|
+
date: 2009-04-05 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|