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