1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
8 * Author: Loic Dachary <loic@dachary.org>
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)
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.
24 #include "gtest/gtest.h"
25 #include "common/Thread.h"
26 #include "common/sharedptr_registry.hpp"
27 #include "common/ceph_argparse.h"
29 class SharedPtrRegistryTest : public SharedPtrRegistry<unsigned int, int> {
31 Mutex &get_lock() { return lock; }
32 map<unsigned int, pair<ceph::weak_ptr<int>, int*> > &get_contents() {
37 class SharedPtrRegistry_all : public ::testing::Test {
40 class Thread_wait : public Thread {
42 SharedPtrRegistryTest ®istry;
45 ceph::shared_ptr<int> ptr;
46 enum in_method_t { LOOKUP, LOOKUP_OR_CREATE } in_method;
48 Thread_wait(SharedPtrRegistryTest& _registry, unsigned int _key, int _value, in_method_t _in_method) :
56 void *entry() override {
58 case LOOKUP_OR_CREATE:
60 ptr = registry.lookup_or_create<int>(key, value);
62 ptr = registry.lookup_or_create(key);
65 ptr = ceph::shared_ptr<int>(new int);
67 ptr = registry.lookup(key);
74 static const useconds_t DELAY_MAX = 20 * 1000 * 1000;
75 static useconds_t delay;
77 bool wait_for(SharedPtrRegistryTest ®istry, int waiting) {
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
88 Mutex::Locker l(registry.get_lock());
89 if (registry.waiting == waiting)
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;
99 useconds_t SharedPtrRegistry_all::delay = 0;
101 TEST_F(SharedPtrRegistry_all, lookup_or_create) {
102 SharedPtrRegistryTest registry;
103 unsigned int key = 1;
105 ceph::shared_ptr<int> ptr = registry.lookup_or_create(key);
107 ASSERT_EQ(value, *registry.lookup_or_create(key));
110 TEST_F(SharedPtrRegistry_all, wait_lookup_or_create) {
111 SharedPtrRegistryTest registry;
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
124 unsigned int key = 1;
126 ceph::shared_ptr<int> ptr(new int);
127 registry.get_contents()[key] = make_pair(ptr, ptr.get());
129 EXPECT_FALSE(registry.get_contents()[key].first.lock());
131 Thread_wait t(registry, key, 0, Thread_wait::LOOKUP_OR_CREATE);
132 t.create("wait_lookcreate");
133 ASSERT_TRUE(wait_for(registry, 1));
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));
140 EXPECT_TRUE(t.ptr.get());
143 unsigned int key = 2;
146 ceph::shared_ptr<int> ptr(new int);
147 registry.get_contents()[key] = make_pair(ptr, ptr.get());
149 EXPECT_FALSE(registry.get_contents()[key].first.lock());
151 Thread_wait t(registry, key, value, Thread_wait::LOOKUP_OR_CREATE);
152 t.create("wait_lookcreate");
153 ASSERT_TRUE(wait_for(registry, 1));
155 // waiting on a key does not block lookups on other keys
157 int other_value = value + 1;
158 unsigned int other_key = key + 1;
159 ceph::shared_ptr<int> ptr = registry.lookup_or_create<int>(other_key, other_value);
160 EXPECT_TRUE(ptr.get());
161 EXPECT_EQ(other_value, *ptr);
163 registry.remove(key);
164 ASSERT_TRUE(wait_for(registry, 0));
166 EXPECT_TRUE(t.ptr.get());
167 EXPECT_EQ(value, *t.ptr);
171 TEST_F(SharedPtrRegistry_all, lookup) {
172 SharedPtrRegistryTest registry;
173 unsigned int key = 1;
175 ceph::shared_ptr<int> ptr = registry.lookup_or_create(key);
178 ASSERT_EQ(value, *registry.lookup(key));
180 ASSERT_FALSE(registry.lookup(key));
183 TEST_F(SharedPtrRegistry_all, wait_lookup) {
184 SharedPtrRegistryTest registry;
186 unsigned int key = 1;
189 ceph::shared_ptr<int> ptr(new int);
190 registry.get_contents()[key] = make_pair(ptr, ptr.get());
192 EXPECT_FALSE(registry.get_contents()[key].first.lock());
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));
206 TEST_F(SharedPtrRegistry_all, get_next) {
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));
215 SharedPtrRegistryTest registry;
217 const unsigned int key2 = 333;
218 ceph::shared_ptr<int> ptr2 = registry.lookup_or_create(key2);
219 const int value2 = *ptr2 = 400;
221 // entries with expired pointers are silentely ignored
222 const unsigned int key_gone = 222;
223 registry.get_contents()[key_gone] = make_pair(ceph::shared_ptr<int>(), (int*)0);
225 const unsigned int key1 = 111;
226 ceph::shared_ptr<int> ptr1 = registry.lookup_or_create(key1);
227 const int value1 = *ptr1 = 800;
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);
234 EXPECT_TRUE(registry.get_next(i.first, &i));
235 EXPECT_EQ(key2, i.first);
236 EXPECT_EQ(value2, i.second);
238 EXPECT_FALSE(registry.get_next(i.first, &i));
242 // http://tracker.ceph.com/issues/6117
243 // reproduce the issue.
245 SharedPtrRegistryTest registry;
246 const unsigned int key1 = 111;
247 ceph::shared_ptr<int> *ptr1 = new ceph::shared_ptr<int>(registry.lookup_or_create(key1));
248 const unsigned int key2 = 222;
249 ceph::shared_ptr<int> ptr2 = registry.lookup_or_create(key2);
251 pair<unsigned int, ceph::shared_ptr<int> > i;
252 EXPECT_TRUE(registry.get_next(i.first, &i));
253 EXPECT_EQ(key1, i.first);
255 EXPECT_TRUE(registry.get_next(i.first, &i));
256 EXPECT_EQ(key2, i.first);
260 TEST_F(SharedPtrRegistry_all, remove) {
262 SharedPtrRegistryTest registry;
263 const unsigned int key1 = 1;
264 ceph::shared_ptr<int> ptr1 = registry.lookup_or_create(key1);
266 registry.remove(key1);
268 ceph::shared_ptr<int> ptr2 = registry.lookup_or_create(key1);
271 ptr1 = ceph::shared_ptr<int>();
272 ceph::shared_ptr<int> res = registry.lookup(key1);
278 SharedPtrRegistryTest registry;
279 const unsigned int key1 = 1;
280 ceph::shared_ptr<int> ptr1 = registry.lookup_or_create(key1, 400);
281 registry.remove(key1);
283 ceph::shared_ptr<int> ptr2 = registry.lookup_or_create(key1, 500);
285 ptr1 = ceph::shared_ptr<int>();
286 ceph::shared_ptr<int> res = registry.lookup(key1);
293 class SharedPtrRegistry_destructor : public ::testing::Test {
296 typedef enum { UNDEFINED, YES, NO } DieEnum;
300 TellDie() { died = NO; }
301 ~TellDie() { died = YES; }
306 void SetUp() override {
311 SharedPtrRegistry_destructor::DieEnum SharedPtrRegistry_destructor::died = SharedPtrRegistry_destructor::UNDEFINED;
313 TEST_F(SharedPtrRegistry_destructor, destructor) {
314 SharedPtrRegistry<int,TellDie> registry;
315 EXPECT_EQ(UNDEFINED, died);
318 ceph::shared_ptr<TellDie> a = registry.lookup_or_create(key);
320 EXPECT_TRUE(a.get());
322 EXPECT_EQ(YES, died);
323 EXPECT_FALSE(registry.lookup(key));
327 // compile-command: "cd ../.. ; make unittest_sharedptr_registry && ./unittest_sharedptr_registry # --gtest_filter=*.* --log-to-stderr=true"