Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / common / test_sharedptr_registry.cc
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:
31   Mutex &get_lock() { return lock; }
32   map<unsigned int, pair<ceph::weak_ptr<int>, int*> > &get_contents() {
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 &registry;
43     unsigned int key;
44     int value;
45     ceph::shared_ptr<int> ptr;
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:
65         ptr = ceph::shared_ptr<int>(new int);
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 &registry, 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       {
88         Mutex::Locker l(registry.get_lock());
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;
105   ceph::shared_ptr<int> ptr = registry.lookup_or_create(key);
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     {
126       ceph::shared_ptr<int> ptr(new int);
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     {
146       ceph::shared_ptr<int> ptr(new int);
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;
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);
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   {
175     ceph::shared_ptr<int> ptr = registry.lookup_or_create(key);
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   {
189     ceph::shared_ptr<int> ptr(new int);
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;
218     ceph::shared_ptr<int> ptr2 = registry.lookup_or_create(key2);
219     const int value2 = *ptr2 = 400;
220
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);
224
225     const unsigned int key1 = 111;
226     ceph::shared_ptr<int> ptr1 = registry.lookup_or_create(key1);
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;
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);
250     
251     pair<unsigned int, ceph::shared_ptr<int> > i;
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;
264     ceph::shared_ptr<int> ptr1 = registry.lookup_or_create(key1);
265     *ptr1 = 400;
266     registry.remove(key1);
267
268     ceph::shared_ptr<int> ptr2 = registry.lookup_or_create(key1);
269     *ptr2 = 500;
270
271     ptr1 = ceph::shared_ptr<int>();
272     ceph::shared_ptr<int> res = registry.lookup(key1);
273     assert(res);
274     assert(res == ptr2);
275     assert(*res == 500);
276   }
277   {
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);
282
283     ceph::shared_ptr<int> ptr2 = registry.lookup_or_create(key1, 500);
284
285     ptr1 = ceph::shared_ptr<int>();
286     ceph::shared_ptr<int> res = registry.lookup(key1);
287     assert(res);
288     assert(res == ptr2);
289     assert(*res == 500);
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     
303     int value;
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   {
318     ceph::shared_ptr<TellDie> a = registry.lookup_or_create(key);
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: