localmemcache 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +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
|
|