]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com> | |
7 | * | |
8 | * Author: Loic Dachary <loic@dachary.org> | |
9 | * Cheng Cheng <ccheng.leo@gmail.com> | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU Library Public License as published by | |
13 | * the Free Software Foundation; either version 2, or (at your option) | |
14 | * any later version. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU Library Public License for more details. | |
20 | * | |
21 | */ | |
22 | ||
23 | #include <stdio.h> | |
24 | #include <signal.h> | |
25 | #include "gtest/gtest.h" | |
26 | #include "common/Thread.h" | |
27 | #include "common/shared_cache.hpp" | |
28 | ||
29 | class SharedLRUTest : public SharedLRU<unsigned int, int> { | |
30 | public: | |
11fdf7f2 TL |
31 | auto& get_lock() { return lock; } |
32 | auto& get_cond() { return cond; } | |
33 | map<unsigned int, pair< std::weak_ptr<int>, int* > > &get_weak_refs() { | |
7c673cae FG |
34 | return weak_refs; |
35 | } | |
36 | }; | |
37 | ||
38 | class SharedLRU_all : public ::testing::Test { | |
39 | public: | |
40 | ||
41 | class Thread_wait : public Thread { | |
42 | public: | |
43 | SharedLRUTest &cache; | |
44 | unsigned int key; | |
45 | int value; | |
11fdf7f2 | 46 | std::shared_ptr<int> ptr; |
7c673cae FG |
47 | enum in_method_t { LOOKUP, LOWER_BOUND } in_method; |
48 | ||
49 | Thread_wait(SharedLRUTest& _cache, unsigned int _key, | |
50 | int _value, in_method_t _in_method) : | |
51 | cache(_cache), | |
52 | key(_key), | |
53 | value(_value), | |
54 | in_method(_in_method) { } | |
55 | ||
56 | void * entry() override { | |
57 | switch (in_method) { | |
58 | case LOWER_BOUND: | |
59 | ptr = cache.lower_bound(key); | |
60 | break; | |
61 | case LOOKUP: | |
11fdf7f2 | 62 | ptr = std::shared_ptr<int>(new int); |
7c673cae FG |
63 | *ptr = value; |
64 | ptr = cache.lookup(key); | |
65 | break; | |
66 | } | |
67 | return NULL; | |
68 | } | |
69 | }; | |
70 | ||
71 | static const useconds_t DELAY_MAX = 20 * 1000 * 1000; | |
72 | static useconds_t delay; | |
73 | ||
74 | bool wait_for(SharedLRUTest &cache, int waitting) { | |
75 | do { | |
76 | // | |
77 | // the delay variable is supposed to be initialized to zero. It would be fine | |
78 | // to usleep(0) but we take this opportunity to test the loop. It will try | |
79 | // again and therefore show that the logic ( increasing the delay ) actually | |
80 | // works. | |
81 | // | |
82 | if (delay > 0) | |
83 | usleep(delay); | |
84 | { | |
11fdf7f2 | 85 | std::lock_guard l{cache.get_lock()}; |
7c673cae FG |
86 | if (cache.waiting == waitting) { |
87 | break; | |
88 | } | |
89 | } | |
90 | if (delay > 0) { | |
91 | cout << "delay " << delay << "us, is not long enough, try again\n"; | |
92 | } | |
93 | } while ((delay = delay * 2 + 1) < DELAY_MAX); | |
94 | return delay < DELAY_MAX; | |
95 | } | |
96 | }; | |
97 | ||
98 | useconds_t SharedLRU_all::delay = 0; | |
99 | ||
100 | TEST_F(SharedLRU_all, add) { | |
101 | SharedLRUTest cache; | |
102 | unsigned int key = 1; | |
103 | int value1 = 2; | |
104 | bool existed = false; | |
105 | { | |
11fdf7f2 | 106 | std::shared_ptr<int> ptr = cache.add(key, new int(value1), &existed); |
7c673cae FG |
107 | ASSERT_EQ(value1, *ptr); |
108 | ASSERT_FALSE(existed); | |
109 | } | |
110 | { | |
111 | int value2 = 3; | |
11fdf7f2 TL |
112 | auto p = new int(value2); |
113 | std::shared_ptr<int> ptr = cache.add(key, p, &existed); | |
7c673cae FG |
114 | ASSERT_EQ(value1, *ptr); |
115 | ASSERT_TRUE(existed); | |
11fdf7f2 | 116 | delete p; |
7c673cae FG |
117 | } |
118 | } | |
119 | TEST_F(SharedLRU_all, empty) { | |
120 | SharedLRUTest cache; | |
121 | unsigned int key = 1; | |
122 | bool existed = false; | |
123 | ||
124 | ASSERT_TRUE(cache.empty()); | |
125 | { | |
126 | int value1 = 2; | |
11fdf7f2 | 127 | std::shared_ptr<int> ptr = cache.add(key, new int(value1), &existed); |
7c673cae FG |
128 | ASSERT_EQ(value1, *ptr); |
129 | ASSERT_FALSE(existed); | |
130 | } | |
131 | ASSERT_FALSE(cache.empty()); | |
132 | ||
133 | cache.clear(key); | |
134 | ASSERT_TRUE(cache.empty()); | |
135 | } | |
136 | ||
137 | TEST_F(SharedLRU_all, lookup) { | |
138 | SharedLRUTest cache; | |
139 | unsigned int key = 1; | |
140 | { | |
141 | int value = 2; | |
142 | ASSERT_TRUE(cache.add(key, new int(value)).get()); | |
143 | ASSERT_TRUE(cache.lookup(key).get()); | |
144 | ASSERT_EQ(value, *cache.lookup(key)); | |
145 | } | |
146 | ASSERT_TRUE(cache.lookup(key).get()); | |
147 | } | |
148 | TEST_F(SharedLRU_all, lookup_or_create) { | |
149 | SharedLRUTest cache; | |
150 | { | |
151 | int value = 2; | |
152 | unsigned int key = 1; | |
153 | ASSERT_TRUE(cache.add(key, new int(value)).get()); | |
154 | ASSERT_TRUE(cache.lookup_or_create(key).get()); | |
155 | ASSERT_EQ(value, *cache.lookup(key)); | |
156 | } | |
157 | { | |
158 | unsigned int key = 2; | |
159 | ASSERT_TRUE(cache.lookup_or_create(key).get()); | |
160 | ASSERT_EQ(0, *cache.lookup(key)); | |
161 | } | |
162 | ASSERT_TRUE(cache.lookup(1).get()); | |
163 | ASSERT_TRUE(cache.lookup(2).get()); | |
164 | } | |
165 | ||
166 | TEST_F(SharedLRU_all, wait_lookup) { | |
167 | SharedLRUTest cache; | |
168 | unsigned int key = 1; | |
169 | int value = 2; | |
170 | ||
171 | { | |
11fdf7f2 | 172 | std::shared_ptr<int> ptr(new int); |
7c673cae FG |
173 | cache.get_weak_refs()[key] = make_pair(ptr, &*ptr); |
174 | } | |
175 | EXPECT_FALSE(cache.get_weak_refs()[key].first.lock()); | |
176 | ||
177 | Thread_wait t(cache, key, value, Thread_wait::LOOKUP); | |
178 | t.create("wait_lookup_1"); | |
179 | ASSERT_TRUE(wait_for(cache, 1)); | |
180 | EXPECT_EQ(value, *t.ptr); | |
181 | // waiting on a key does not block lookups on other keys | |
182 | EXPECT_FALSE(cache.lookup(key + 12345)); | |
183 | { | |
11fdf7f2 | 184 | std::lock_guard l{cache.get_lock()}; |
7c673cae | 185 | cache.get_weak_refs().erase(key); |
11fdf7f2 | 186 | cache.get_cond().notify_one(); |
7c673cae FG |
187 | } |
188 | ASSERT_TRUE(wait_for(cache, 0)); | |
189 | t.join(); | |
190 | EXPECT_FALSE(t.ptr); | |
191 | } | |
192 | TEST_F(SharedLRU_all, wait_lookup_or_create) { | |
193 | SharedLRUTest cache; | |
194 | unsigned int key = 1; | |
195 | int value = 2; | |
196 | ||
197 | { | |
11fdf7f2 | 198 | std::shared_ptr<int> ptr(new int); |
7c673cae FG |
199 | cache.get_weak_refs()[key] = make_pair(ptr, &*ptr); |
200 | } | |
201 | EXPECT_FALSE(cache.get_weak_refs()[key].first.lock()); | |
202 | ||
203 | Thread_wait t(cache, key, value, Thread_wait::LOOKUP); | |
204 | t.create("wait_lookup_2"); | |
205 | ASSERT_TRUE(wait_for(cache, 1)); | |
206 | EXPECT_EQ(value, *t.ptr); | |
207 | // waiting on a key does not block lookups on other keys | |
208 | EXPECT_TRUE(cache.lookup_or_create(key + 12345).get()); | |
209 | { | |
11fdf7f2 | 210 | std::lock_guard l{cache.get_lock()}; |
7c673cae | 211 | cache.get_weak_refs().erase(key); |
11fdf7f2 | 212 | cache.get_cond().notify_one(); |
7c673cae FG |
213 | } |
214 | ASSERT_TRUE(wait_for(cache, 0)); | |
215 | t.join(); | |
216 | EXPECT_FALSE(t.ptr); | |
217 | } | |
218 | ||
219 | TEST_F(SharedLRU_all, lower_bound) { | |
220 | SharedLRUTest cache; | |
221 | ||
222 | { | |
223 | unsigned int key = 1; | |
224 | ASSERT_FALSE(cache.lower_bound(key)); | |
225 | int value = 2; | |
226 | ||
227 | ASSERT_TRUE(cache.add(key, new int(value)).get()); | |
228 | ASSERT_TRUE(cache.lower_bound(key).get()); | |
229 | EXPECT_EQ(value, *cache.lower_bound(key)); | |
230 | } | |
231 | } | |
232 | ||
233 | TEST_F(SharedLRU_all, wait_lower_bound) { | |
234 | SharedLRUTest cache; | |
235 | unsigned int key = 1; | |
236 | int value = 2; | |
237 | unsigned int other_key = key + 1; | |
238 | int other_value = value + 1; | |
239 | ||
240 | ASSERT_TRUE(cache.add(other_key, new int(other_value)).get()); | |
241 | ||
242 | { | |
11fdf7f2 | 243 | std::shared_ptr<int> ptr(new int); |
7c673cae FG |
244 | cache.get_weak_refs()[key] = make_pair(ptr, &*ptr); |
245 | } | |
246 | EXPECT_FALSE(cache.get_weak_refs()[key].first.lock()); | |
247 | ||
248 | Thread_wait t(cache, key, value, Thread_wait::LOWER_BOUND); | |
249 | t.create("wait_lower_bnd"); | |
250 | ASSERT_TRUE(wait_for(cache, 1)); | |
251 | EXPECT_FALSE(t.ptr); | |
252 | // waiting on a key does not block getting lower_bound on other keys | |
253 | EXPECT_TRUE(cache.lower_bound(other_key).get()); | |
254 | { | |
11fdf7f2 | 255 | std::lock_guard l{cache.get_lock()}; |
7c673cae | 256 | cache.get_weak_refs().erase(key); |
11fdf7f2 | 257 | cache.get_cond().notify_one(); |
7c673cae FG |
258 | } |
259 | ASSERT_TRUE(wait_for(cache, 0)); | |
260 | t.join(); | |
261 | EXPECT_TRUE(t.ptr.get()); | |
262 | } | |
263 | TEST_F(SharedLRU_all, get_next) { | |
264 | ||
265 | { | |
266 | SharedLRUTest cache; | |
267 | const unsigned int key = 0; | |
268 | pair<unsigned int, int> i; | |
269 | EXPECT_FALSE(cache.get_next(key, &i)); | |
270 | } | |
271 | { | |
272 | SharedLRUTest cache; | |
273 | ||
274 | const unsigned int key2 = 333; | |
11fdf7f2 | 275 | std::shared_ptr<int> ptr2 = cache.lookup_or_create(key2); |
7c673cae FG |
276 | const int value2 = *ptr2 = 400; |
277 | ||
278 | // entries with expired pointers are silently ignored | |
279 | const unsigned int key_gone = 222; | |
11fdf7f2 | 280 | cache.get_weak_refs()[key_gone] = make_pair(std::shared_ptr<int>(), (int*)0); |
7c673cae FG |
281 | |
282 | const unsigned int key1 = 111; | |
11fdf7f2 | 283 | std::shared_ptr<int> ptr1 = cache.lookup_or_create(key1); |
7c673cae FG |
284 | const int value1 = *ptr1 = 800; |
285 | ||
286 | pair<unsigned int, int> i; | |
287 | EXPECT_TRUE(cache.get_next(0, &i)); | |
288 | EXPECT_EQ(key1, i.first); | |
289 | EXPECT_EQ(value1, i.second); | |
290 | ||
291 | EXPECT_TRUE(cache.get_next(i.first, &i)); | |
292 | EXPECT_EQ(key2, i.first); | |
293 | EXPECT_EQ(value2, i.second); | |
294 | ||
295 | EXPECT_FALSE(cache.get_next(i.first, &i)); | |
296 | ||
297 | cache.get_weak_refs().clear(); | |
298 | } | |
299 | { | |
300 | SharedLRUTest cache; | |
301 | const unsigned int key1 = 111; | |
11fdf7f2 | 302 | std::shared_ptr<int> *ptr1 = new shared_ptr<int>(cache.lookup_or_create(key1)); |
7c673cae | 303 | const unsigned int key2 = 222; |
11fdf7f2 | 304 | std::shared_ptr<int> ptr2 = cache.lookup_or_create(key2); |
7c673cae | 305 | |
11fdf7f2 | 306 | pair<unsigned int, std::shared_ptr<int> > i; |
7c673cae FG |
307 | EXPECT_TRUE(cache.get_next(i.first, &i)); |
308 | EXPECT_EQ(key1, i.first); | |
309 | delete ptr1; | |
310 | EXPECT_TRUE(cache.get_next(i.first, &i)); | |
311 | EXPECT_EQ(key2, i.first); | |
312 | } | |
313 | } | |
314 | ||
315 | TEST_F(SharedLRU_all, clear) { | |
316 | SharedLRUTest cache; | |
317 | unsigned int key = 1; | |
318 | int value = 2; | |
319 | { | |
11fdf7f2 | 320 | std::shared_ptr<int> ptr = cache.add(key, new int(value)); |
7c673cae FG |
321 | ASSERT_EQ(value, *cache.lookup(key)); |
322 | } | |
323 | ASSERT_TRUE(cache.lookup(key).get()); | |
324 | cache.clear(key); | |
325 | ASSERT_FALSE(cache.lookup(key)); | |
326 | ||
327 | { | |
11fdf7f2 | 328 | std::shared_ptr<int> ptr = cache.add(key, new int(value)); |
7c673cae FG |
329 | } |
330 | ASSERT_TRUE(cache.lookup(key).get()); | |
331 | cache.clear(key); | |
332 | ASSERT_FALSE(cache.lookup(key)); | |
333 | } | |
334 | TEST_F(SharedLRU_all, clear_all) { | |
335 | SharedLRUTest cache; | |
336 | unsigned int key = 1; | |
337 | int value = 2; | |
338 | { | |
11fdf7f2 | 339 | std::shared_ptr<int> ptr = cache.add(key, new int(value)); |
7c673cae FG |
340 | ASSERT_EQ(value, *cache.lookup(key)); |
341 | } | |
342 | ASSERT_TRUE(cache.lookup(key).get()); | |
343 | cache.clear(); | |
344 | ASSERT_FALSE(cache.lookup(key)); | |
345 | ||
11fdf7f2 | 346 | std::shared_ptr<int> ptr2 = cache.add(key, new int(value)); |
7c673cae FG |
347 | ASSERT_TRUE(cache.lookup(key).get()); |
348 | cache.clear(); | |
349 | ASSERT_TRUE(cache.lookup(key).get()); | |
350 | ASSERT_FALSE(cache.empty()); | |
351 | } | |
352 | ||
353 | TEST(SharedCache_all, add) { | |
354 | SharedLRU<int, int> cache; | |
355 | unsigned int key = 1; | |
356 | int value = 2; | |
11fdf7f2 | 357 | std::shared_ptr<int> ptr = cache.add(key, new int(value)); |
7c673cae FG |
358 | ASSERT_EQ(ptr, cache.lookup(key)); |
359 | ASSERT_EQ(value, *cache.lookup(key)); | |
360 | } | |
361 | ||
362 | TEST(SharedCache_all, lru) { | |
363 | const size_t SIZE = 5; | |
364 | SharedLRU<int, int> cache(NULL, SIZE); | |
365 | ||
366 | bool existed = false; | |
11fdf7f2 | 367 | std::shared_ptr<int> ptr = cache.add(0, new int(0), &existed); |
7c673cae FG |
368 | ASSERT_FALSE(existed); |
369 | { | |
370 | int *tmpint = new int(0); | |
11fdf7f2 | 371 | std::shared_ptr<int> ptr2 = cache.add(0, tmpint, &existed); |
7c673cae FG |
372 | ASSERT_TRUE(existed); |
373 | delete tmpint; | |
374 | } | |
375 | for (size_t i = 1; i < 2*SIZE; ++i) { | |
376 | cache.add(i, new int(i), &existed); | |
377 | ASSERT_FALSE(existed); | |
378 | } | |
379 | ||
380 | ASSERT_TRUE(cache.lookup(0).get()); | |
381 | ASSERT_EQ(0, *cache.lookup(0)); | |
382 | ||
383 | ASSERT_FALSE(cache.lookup(SIZE-1)); | |
384 | ASSERT_FALSE(cache.lookup(SIZE)); | |
385 | ASSERT_TRUE(cache.lookup(SIZE+1).get()); | |
386 | ASSERT_EQ((int)SIZE+1, *cache.lookup(SIZE+1)); | |
387 | ||
388 | cache.purge(0); | |
389 | ASSERT_FALSE(cache.lookup(0)); | |
11fdf7f2 | 390 | std::shared_ptr<int> ptr2 = cache.add(0, new int(0), &existed); |
7c673cae | 391 | ASSERT_FALSE(ptr == ptr2); |
11fdf7f2 | 392 | ptr = std::shared_ptr<int>(); |
7c673cae FG |
393 | ASSERT_TRUE(cache.lookup(0).get()); |
394 | } | |
395 | ||
396 | // Local Variables: | |
397 | // compile-command: "cd ../.. ; make unittest_shared_cache && ./unittest_shared_cache # --gtest_filter=*.* --log-to-stderr=true" | |
398 | // End: |