]>
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 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU Library Public License as published by | |
12 | * the Free Software Foundation; either version 2, or (at your option) | |
13 | * any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU Library Public License for more details. | |
19 | * | |
20 | */ | |
21 | ||
22 | #include <stdio.h> | |
23 | #include <signal.h> | |
24 | #include "gtest/gtest.h" | |
25 | #include "common/Thread.h" | |
26 | #include "common/sharedptr_registry.hpp" | |
27 | #include "common/ceph_argparse.h" | |
28 | ||
20effc67 TL |
29 | using namespace std; |
30 | ||
7c673cae FG |
31 | class SharedPtrRegistryTest : public SharedPtrRegistry<unsigned int, int> { |
32 | public: | |
11fdf7f2 TL |
33 | ceph::mutex &get_lock() { return lock; } |
34 | map<unsigned int, pair<std::weak_ptr<int>, int*> > &get_contents() { | |
7c673cae FG |
35 | return contents; |
36 | } | |
37 | }; | |
38 | ||
39 | class SharedPtrRegistry_all : public ::testing::Test { | |
40 | public: | |
41 | ||
42 | class Thread_wait : public Thread { | |
43 | public: | |
44 | SharedPtrRegistryTest ®istry; | |
45 | unsigned int key; | |
46 | int value; | |
11fdf7f2 | 47 | std::shared_ptr<int> ptr; |
7c673cae FG |
48 | enum in_method_t { LOOKUP, LOOKUP_OR_CREATE } in_method; |
49 | ||
50 | Thread_wait(SharedPtrRegistryTest& _registry, unsigned int _key, int _value, in_method_t _in_method) : | |
51 | registry(_registry), | |
52 | key(_key), | |
53 | value(_value), | |
54 | in_method(_in_method) | |
55 | { | |
56 | } | |
57 | ||
58 | void *entry() override { | |
59 | switch(in_method) { | |
60 | case LOOKUP_OR_CREATE: | |
61 | if (value) | |
62 | ptr = registry.lookup_or_create<int>(key, value); | |
63 | else | |
64 | ptr = registry.lookup_or_create(key); | |
65 | break; | |
66 | case LOOKUP: | |
11fdf7f2 | 67 | ptr = std::shared_ptr<int>(new int); |
7c673cae FG |
68 | *ptr = value; |
69 | ptr = registry.lookup(key); | |
70 | break; | |
71 | } | |
72 | return NULL; | |
73 | } | |
74 | }; | |
75 | ||
76 | static const useconds_t DELAY_MAX = 20 * 1000 * 1000; | |
77 | static useconds_t delay; | |
78 | ||
79 | bool wait_for(SharedPtrRegistryTest ®istry, int waiting) { | |
80 | do { | |
81 | // | |
82 | // the delay variable is supposed to be initialized to zero. It would be fine | |
83 | // to usleep(0) but we take this opportunity to test the loop. It will try | |
84 | // again and therefore show that the logic ( increasing the delay ) actually | |
85 | // works. | |
86 | // | |
87 | if (delay > 0) | |
88 | usleep(delay); | |
89 | { | |
11fdf7f2 | 90 | std::lock_guard l(registry.get_lock()); |
7c673cae FG |
91 | if (registry.waiting == waiting) |
92 | break; | |
93 | } | |
94 | if (delay > 0) | |
95 | cout << "delay " << delay << "us, is not long enough, try again\n"; | |
96 | } while (( delay = delay * 2 + 1) < DELAY_MAX); | |
97 | return delay < DELAY_MAX; | |
98 | } | |
99 | }; | |
100 | ||
101 | useconds_t SharedPtrRegistry_all::delay = 0; | |
102 | ||
103 | TEST_F(SharedPtrRegistry_all, lookup_or_create) { | |
104 | SharedPtrRegistryTest registry; | |
105 | unsigned int key = 1; | |
106 | int value = 2; | |
11fdf7f2 | 107 | std::shared_ptr<int> ptr = registry.lookup_or_create(key); |
7c673cae FG |
108 | *ptr = value; |
109 | ASSERT_EQ(value, *registry.lookup_or_create(key)); | |
110 | } | |
111 | ||
112 | TEST_F(SharedPtrRegistry_all, wait_lookup_or_create) { | |
113 | SharedPtrRegistryTest registry; | |
114 | ||
115 | // | |
116 | // simulate the following: The last reference to a shared_ptr goes | |
117 | // out of scope and the shared_ptr object is about to be removed and | |
118 | // marked as such. The weak_ptr stored in the registry will show | |
119 | // that it has expired(). However, the SharedPtrRegistry::OnRemoval | |
120 | // object has not yet been called and did not get a chance to | |
121 | // acquire the lock. The lookup_or_create and lookup methods must | |
122 | // detect that situation and wait until the weak_ptr is removed from | |
123 | // the registry. | |
124 | // | |
125 | { | |
126 | unsigned int key = 1; | |
127 | { | |
11fdf7f2 | 128 | std::shared_ptr<int> ptr(new int); |
7c673cae FG |
129 | registry.get_contents()[key] = make_pair(ptr, ptr.get()); |
130 | } | |
131 | EXPECT_FALSE(registry.get_contents()[key].first.lock()); | |
132 | ||
133 | Thread_wait t(registry, key, 0, Thread_wait::LOOKUP_OR_CREATE); | |
134 | t.create("wait_lookcreate"); | |
135 | ASSERT_TRUE(wait_for(registry, 1)); | |
136 | EXPECT_FALSE(t.ptr); | |
137 | // waiting on a key does not block lookups on other keys | |
138 | EXPECT_TRUE(registry.lookup_or_create(key + 12345).get()); | |
139 | registry.remove(key); | |
140 | ASSERT_TRUE(wait_for(registry, 0)); | |
141 | t.join(); | |
142 | EXPECT_TRUE(t.ptr.get()); | |
143 | } | |
144 | { | |
145 | unsigned int key = 2; | |
146 | int value = 3; | |
147 | { | |
11fdf7f2 | 148 | std::shared_ptr<int> ptr(new int); |
7c673cae FG |
149 | registry.get_contents()[key] = make_pair(ptr, ptr.get()); |
150 | } | |
151 | EXPECT_FALSE(registry.get_contents()[key].first.lock()); | |
152 | ||
153 | Thread_wait t(registry, key, value, Thread_wait::LOOKUP_OR_CREATE); | |
154 | t.create("wait_lookcreate"); | |
155 | ASSERT_TRUE(wait_for(registry, 1)); | |
156 | EXPECT_FALSE(t.ptr); | |
157 | // waiting on a key does not block lookups on other keys | |
158 | { | |
159 | int other_value = value + 1; | |
160 | unsigned int other_key = key + 1; | |
11fdf7f2 | 161 | std::shared_ptr<int> ptr = registry.lookup_or_create<int>(other_key, other_value); |
7c673cae FG |
162 | EXPECT_TRUE(ptr.get()); |
163 | EXPECT_EQ(other_value, *ptr); | |
164 | } | |
165 | registry.remove(key); | |
166 | ASSERT_TRUE(wait_for(registry, 0)); | |
167 | t.join(); | |
168 | EXPECT_TRUE(t.ptr.get()); | |
169 | EXPECT_EQ(value, *t.ptr); | |
170 | } | |
171 | } | |
172 | ||
173 | TEST_F(SharedPtrRegistry_all, lookup) { | |
174 | SharedPtrRegistryTest registry; | |
175 | unsigned int key = 1; | |
176 | { | |
11fdf7f2 | 177 | std::shared_ptr<int> ptr = registry.lookup_or_create(key); |
7c673cae FG |
178 | int value = 2; |
179 | *ptr = value; | |
180 | ASSERT_EQ(value, *registry.lookup(key)); | |
181 | } | |
182 | ASSERT_FALSE(registry.lookup(key)); | |
183 | } | |
184 | ||
185 | TEST_F(SharedPtrRegistry_all, wait_lookup) { | |
186 | SharedPtrRegistryTest registry; | |
187 | ||
188 | unsigned int key = 1; | |
189 | int value = 2; | |
190 | { | |
11fdf7f2 | 191 | std::shared_ptr<int> ptr(new int); |
7c673cae FG |
192 | registry.get_contents()[key] = make_pair(ptr, ptr.get()); |
193 | } | |
194 | EXPECT_FALSE(registry.get_contents()[key].first.lock()); | |
195 | ||
196 | Thread_wait t(registry, key, value, Thread_wait::LOOKUP); | |
197 | t.create("wait_lookup"); | |
198 | ASSERT_TRUE(wait_for(registry, 1)); | |
199 | EXPECT_EQ(value, *t.ptr); | |
200 | // waiting on a key does not block lookups on other keys | |
201 | EXPECT_FALSE(registry.lookup(key + 12345)); | |
202 | registry.remove(key); | |
203 | ASSERT_TRUE(wait_for(registry, 0)); | |
204 | t.join(); | |
205 | EXPECT_FALSE(t.ptr); | |
206 | } | |
207 | ||
208 | TEST_F(SharedPtrRegistry_all, get_next) { | |
209 | ||
210 | { | |
211 | SharedPtrRegistry<unsigned int,int> registry; | |
212 | const unsigned int key = 0; | |
213 | pair<unsigned int, int> i; | |
214 | EXPECT_FALSE(registry.get_next(key, &i)); | |
215 | } | |
216 | { | |
217 | SharedPtrRegistryTest registry; | |
218 | ||
219 | const unsigned int key2 = 333; | |
11fdf7f2 | 220 | std::shared_ptr<int> ptr2 = registry.lookup_or_create(key2); |
7c673cae FG |
221 | const int value2 = *ptr2 = 400; |
222 | ||
223 | // entries with expired pointers are silentely ignored | |
224 | const unsigned int key_gone = 222; | |
11fdf7f2 | 225 | registry.get_contents()[key_gone] = make_pair(std::shared_ptr<int>(), (int*)0); |
7c673cae FG |
226 | |
227 | const unsigned int key1 = 111; | |
11fdf7f2 | 228 | std::shared_ptr<int> ptr1 = registry.lookup_or_create(key1); |
7c673cae FG |
229 | const int value1 = *ptr1 = 800; |
230 | ||
231 | pair<unsigned int, int> i; | |
232 | EXPECT_TRUE(registry.get_next(i.first, &i)); | |
233 | EXPECT_EQ(key1, i.first); | |
234 | EXPECT_EQ(value1, i.second); | |
235 | ||
236 | EXPECT_TRUE(registry.get_next(i.first, &i)); | |
237 | EXPECT_EQ(key2, i.first); | |
238 | EXPECT_EQ(value2, i.second); | |
239 | ||
240 | EXPECT_FALSE(registry.get_next(i.first, &i)); | |
241 | } | |
242 | { | |
243 | // | |
244 | // http://tracker.ceph.com/issues/6117 | |
245 | // reproduce the issue. | |
246 | // | |
247 | SharedPtrRegistryTest registry; | |
248 | const unsigned int key1 = 111; | |
11fdf7f2 | 249 | std::shared_ptr<int> *ptr1 = new std::shared_ptr<int>(registry.lookup_or_create(key1)); |
7c673cae | 250 | const unsigned int key2 = 222; |
11fdf7f2 | 251 | std::shared_ptr<int> ptr2 = registry.lookup_or_create(key2); |
7c673cae | 252 | |
11fdf7f2 | 253 | pair<unsigned int, std::shared_ptr<int> > i; |
7c673cae FG |
254 | EXPECT_TRUE(registry.get_next(i.first, &i)); |
255 | EXPECT_EQ(key1, i.first); | |
256 | delete ptr1; | |
257 | EXPECT_TRUE(registry.get_next(i.first, &i)); | |
258 | EXPECT_EQ(key2, i.first); | |
259 | } | |
260 | } | |
261 | ||
262 | TEST_F(SharedPtrRegistry_all, remove) { | |
263 | { | |
264 | SharedPtrRegistryTest registry; | |
265 | const unsigned int key1 = 1; | |
11fdf7f2 | 266 | std::shared_ptr<int> ptr1 = registry.lookup_or_create(key1); |
7c673cae FG |
267 | *ptr1 = 400; |
268 | registry.remove(key1); | |
269 | ||
11fdf7f2 | 270 | std::shared_ptr<int> ptr2 = registry.lookup_or_create(key1); |
7c673cae FG |
271 | *ptr2 = 500; |
272 | ||
11fdf7f2 TL |
273 | ptr1 = std::shared_ptr<int>(); |
274 | std::shared_ptr<int> res = registry.lookup(key1); | |
275 | ceph_assert(res); | |
276 | ceph_assert(res == ptr2); | |
277 | ceph_assert(*res == 500); | |
7c673cae FG |
278 | } |
279 | { | |
280 | SharedPtrRegistryTest registry; | |
281 | const unsigned int key1 = 1; | |
11fdf7f2 | 282 | std::shared_ptr<int> ptr1 = registry.lookup_or_create(key1, 400); |
7c673cae FG |
283 | registry.remove(key1); |
284 | ||
11fdf7f2 | 285 | std::shared_ptr<int> ptr2 = registry.lookup_or_create(key1, 500); |
7c673cae | 286 | |
11fdf7f2 TL |
287 | ptr1 = std::shared_ptr<int>(); |
288 | std::shared_ptr<int> res = registry.lookup(key1); | |
289 | ceph_assert(res); | |
290 | ceph_assert(res == ptr2); | |
291 | ceph_assert(*res == 500); | |
7c673cae FG |
292 | } |
293 | } | |
294 | ||
295 | class SharedPtrRegistry_destructor : public ::testing::Test { | |
296 | public: | |
297 | ||
298 | typedef enum { UNDEFINED, YES, NO } DieEnum; | |
299 | static DieEnum died; | |
300 | ||
301 | struct TellDie { | |
302 | TellDie() { died = NO; } | |
303 | ~TellDie() { died = YES; } | |
304 | ||
11fdf7f2 | 305 | int value = 0; |
7c673cae FG |
306 | }; |
307 | ||
308 | void SetUp() override { | |
309 | died = UNDEFINED; | |
310 | } | |
311 | }; | |
312 | ||
313 | SharedPtrRegistry_destructor::DieEnum SharedPtrRegistry_destructor::died = SharedPtrRegistry_destructor::UNDEFINED; | |
314 | ||
315 | TEST_F(SharedPtrRegistry_destructor, destructor) { | |
316 | SharedPtrRegistry<int,TellDie> registry; | |
317 | EXPECT_EQ(UNDEFINED, died); | |
318 | int key = 101; | |
319 | { | |
11fdf7f2 | 320 | std::shared_ptr<TellDie> a = registry.lookup_or_create(key); |
7c673cae FG |
321 | EXPECT_EQ(NO, died); |
322 | EXPECT_TRUE(a.get()); | |
323 | } | |
324 | EXPECT_EQ(YES, died); | |
325 | EXPECT_FALSE(registry.lookup(key)); | |
326 | } | |
327 | ||
328 | // Local Variables: | |
329 | // compile-command: "cd ../.. ; make unittest_sharedptr_registry && ./unittest_sharedptr_registry # --gtest_filter=*.* --log-to-stderr=true" | |
330 | // End: |