]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/common/test_sharedptr_registry.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / test / common / test_sharedptr_registry.cc
CommitLineData
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
29using namespace std;
30
7c673cae
FG
31class SharedPtrRegistryTest : public SharedPtrRegistry<unsigned int, int> {
32public:
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
39class SharedPtrRegistry_all : public ::testing::Test {
40public:
41
42 class Thread_wait : public Thread {
43 public:
44 SharedPtrRegistryTest &registry;
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 &registry, 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
101useconds_t SharedPtrRegistry_all::delay = 0;
102
103TEST_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
112TEST_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
173TEST_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
185TEST_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
208TEST_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
262TEST_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
295class SharedPtrRegistry_destructor : public ::testing::Test {
296public:
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
313SharedPtrRegistry_destructor::DieEnum SharedPtrRegistry_destructor::died = SharedPtrRegistry_destructor::UNDEFINED;
314
315TEST_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: