f0815d912eca5d4009e6ca21aaf6f9f7d48e565d
[folly.git] / folly / experimental / test / SingletonTest.cpp
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/experimental/Singleton.h>
18
19 #include <folly/Benchmark.h>
20
21 #include <glog/logging.h>
22 #include <gtest/gtest.h>
23
24 using namespace folly;
25
26 // A simple class that tracks how often instances of the class and
27 // subclasses are created, and the ordering.  Also tracks a global
28 // unique counter for each object.
29 std::atomic<size_t> global_counter(19770326);
30 struct Watchdog {
31   static std::vector<Watchdog*> creation_order;
32   Watchdog() : serial_number(++global_counter) {
33     creation_order.push_back(this);
34   }
35
36   ~Watchdog() {
37     if (creation_order.back() != this) {
38       throw std::out_of_range("Watchdog destruction order mismatch");
39     }
40     creation_order.pop_back();
41   }
42
43   const size_t serial_number;
44
45   Watchdog(const Watchdog&) = delete;
46   Watchdog& operator=(const Watchdog&) = delete;
47   Watchdog(Watchdog&&) noexcept = default;
48 };
49
50 std::vector<Watchdog*> Watchdog::creation_order;
51
52 // Some basic types we use for tracking.
53 struct ChildWatchdog : public Watchdog {};
54 struct GlobalWatchdog : public Watchdog {};
55 struct UnregisteredWatchdog : public Watchdog {};
56
57 namespace {
58 Singleton<GlobalWatchdog> global_watchdog;
59 }
60
61 // Test basic global usage (the default way singletons will generally
62 // be used).
63 TEST(Singleton, BasicGlobalUsage) {
64   EXPECT_EQ(Watchdog::creation_order.size(), 0);
65   EXPECT_EQ(SingletonVault::singleton()->registeredSingletonCount(), 1);
66   EXPECT_EQ(SingletonVault::singleton()->livingSingletonCount(), 0);
67   auto wd1 = Singleton<GlobalWatchdog>::get();
68   EXPECT_NE(wd1, nullptr);
69   EXPECT_EQ(Watchdog::creation_order.size(), 1);
70   auto wd2 = Singleton<GlobalWatchdog>::get();
71   EXPECT_NE(wd2, nullptr);
72   EXPECT_EQ(wd1, wd2);
73   EXPECT_EQ(Watchdog::creation_order.size(), 1);
74   SingletonVault::singleton()->destroyInstances();
75   EXPECT_EQ(Watchdog::creation_order.size(), 0);
76 }
77
78 TEST(Singleton, MissingSingleton) {
79   EXPECT_THROW([]() { auto u = Singleton<UnregisteredWatchdog>::get(); }(),
80                std::out_of_range);
81 }
82
83 // Exercise some basic codepaths ensuring registration order and
84 // destruction order happen as expected, that instances are created
85 // when expected, etc etc.
86 TEST(Singleton, BasicUsage) {
87   SingletonVault vault;
88
89   EXPECT_EQ(vault.registeredSingletonCount(), 0);
90   Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault);
91   EXPECT_EQ(vault.registeredSingletonCount(), 1);
92
93   Singleton<ChildWatchdog> child_watchdog_singleton(nullptr, nullptr, &vault);
94   EXPECT_EQ(vault.registeredSingletonCount(), 2);
95
96   vault.registrationComplete();
97
98   Watchdog* s1 = Singleton<Watchdog>::get(&vault);
99   EXPECT_NE(s1, nullptr);
100
101   Watchdog* s2 = Singleton<Watchdog>::get(&vault);
102   EXPECT_NE(s2, nullptr);
103
104   EXPECT_EQ(s1, s2);
105
106   auto s3 = Singleton<ChildWatchdog>::get(&vault);
107   EXPECT_NE(s3, nullptr);
108   EXPECT_NE(s2, s3);
109
110   EXPECT_EQ(vault.registeredSingletonCount(), 2);
111   EXPECT_EQ(vault.livingSingletonCount(), 2);
112
113   vault.destroyInstances();
114   EXPECT_EQ(vault.registeredSingletonCount(), 2);
115   EXPECT_EQ(vault.livingSingletonCount(), 0);
116 }
117
118 // Some pathological cases such as getting unregistered singletons,
119 // double registration, etc.
120 TEST(Singleton, NaughtyUsage) {
121   SingletonVault vault;
122   vault.registrationComplete();
123
124   // Unregistered.
125   EXPECT_THROW(Singleton<Watchdog>::get(), std::out_of_range);
126   EXPECT_THROW(Singleton<Watchdog>::get(&vault), std::out_of_range);
127
128   // Registring singletons after registrationComplete called.
129   EXPECT_THROW([&vault]() {
130                  Singleton<Watchdog> watchdog_singleton(
131                      nullptr, nullptr, &vault);
132                }(),
133                std::logic_error);
134
135   EXPECT_THROW([]() { Singleton<Watchdog> watchdog_singleton; }(),
136                std::logic_error);
137
138   SingletonVault vault_2;
139   EXPECT_THROW(Singleton<Watchdog>::get(&vault_2), std::logic_error);
140   Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault_2);
141   // double registration
142   EXPECT_THROW([&vault_2]() {
143                  Singleton<Watchdog> watchdog_singleton(
144                      nullptr, nullptr, &vault_2);
145                }(),
146                std::logic_error);
147   vault_2.destroyInstances();
148   // double registration after destroy
149   EXPECT_THROW([&vault_2]() {
150                  Singleton<Watchdog> watchdog_singleton(
151                      nullptr, nullptr, &vault_2);
152                }(),
153                std::logic_error);
154 }
155
156 TEST(Singleton, SharedPtrUsage) {
157   SingletonVault vault;
158
159   EXPECT_EQ(vault.registeredSingletonCount(), 0);
160   Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault);
161   EXPECT_EQ(vault.registeredSingletonCount(), 1);
162
163   Singleton<ChildWatchdog> child_watchdog_singleton(nullptr, nullptr, &vault);
164   EXPECT_EQ(vault.registeredSingletonCount(), 2);
165
166   vault.registrationComplete();
167
168   Watchdog* s1 = Singleton<Watchdog>::get(&vault);
169   EXPECT_NE(s1, nullptr);
170
171   Watchdog* s2 = Singleton<Watchdog>::get(&vault);
172   EXPECT_NE(s2, nullptr);
173
174   EXPECT_EQ(s1, s2);
175
176   auto weak_s1 = Singleton<Watchdog>::get_weak(&vault);
177   auto shared_s1 = weak_s1.lock();
178   EXPECT_EQ(shared_s1.get(), s1);
179   EXPECT_EQ(shared_s1.use_count(), 2);
180
181   LOG(ERROR) << "The following log message regarding ref counts is expected";
182   vault.destroyInstances();
183   EXPECT_EQ(vault.registeredSingletonCount(), 2);
184   EXPECT_EQ(vault.livingSingletonCount(), 0);
185
186   EXPECT_EQ(shared_s1.use_count(), 1);
187   EXPECT_EQ(shared_s1.get(), s1);
188
189   auto locked_s1 = weak_s1.lock();
190   EXPECT_EQ(locked_s1.get(), s1);
191   EXPECT_EQ(shared_s1.use_count(), 2);
192   locked_s1.reset();
193   EXPECT_EQ(shared_s1.use_count(), 1);
194
195   // Track serial number rather than pointer since the memory could be
196   // re-used when we create new_s1.
197   auto old_serial = shared_s1->serial_number;
198   shared_s1.reset();
199   locked_s1 = weak_s1.lock();
200   EXPECT_TRUE(weak_s1.expired());
201
202   Watchdog* new_s1 = Singleton<Watchdog>::get(&vault);
203   EXPECT_NE(new_s1->serial_number, old_serial);
204 }
205
206 // Some classes to test singleton dependencies.  NeedySingleton has a
207 // dependency on NeededSingleton, which happens during its
208 // construction.
209 SingletonVault needy_vault;
210
211 struct NeededSingleton {};
212 struct NeedySingleton {
213   NeedySingleton() {
214     auto unused = Singleton<NeededSingleton>::get(&needy_vault);
215     EXPECT_NE(unused, nullptr);
216   }
217 };
218
219 // Ensure circular dependencies fail -- a singleton that needs itself, whoops.
220 SingletonVault self_needy_vault;
221 struct SelfNeedySingleton {
222   SelfNeedySingleton() {
223     auto unused = Singleton<SelfNeedySingleton>::get(&self_needy_vault);
224     EXPECT_NE(unused, nullptr);
225   }
226 };
227
228 TEST(Singleton, SingletonDependencies) {
229   Singleton<NeededSingleton> needed_singleton(nullptr, nullptr, &needy_vault);
230   Singleton<NeedySingleton> needy_singleton(nullptr, nullptr, &needy_vault);
231   needy_vault.registrationComplete();
232
233   EXPECT_EQ(needy_vault.registeredSingletonCount(), 2);
234   EXPECT_EQ(needy_vault.livingSingletonCount(), 0);
235
236   auto needy = Singleton<NeedySingleton>::get(&needy_vault);
237   EXPECT_EQ(needy_vault.livingSingletonCount(), 2);
238
239   Singleton<SelfNeedySingleton> self_needy_singleton(
240       nullptr, nullptr, &self_needy_vault);
241   self_needy_vault.registrationComplete();
242   EXPECT_THROW([]() {
243                  Singleton<SelfNeedySingleton>::get(&self_needy_vault);
244                }(),
245                std::out_of_range);
246 }
247
248 // Benchmarking a normal singleton vs a Meyers singleton vs a Folly
249 // singleton.  Meyers are insanely fast, but (hopefully) Folly
250 // singletons are fast "enough."
251 int* getMeyersSingleton() {
252   static auto ret = new int(0);
253   return ret;
254 }
255
256 int normal_singleton_value = 0;
257 int* getNormalSingleton() {
258   doNotOptimizeAway(&normal_singleton_value);
259   return &normal_singleton_value;
260 }
261
262 struct BenchmarkSingleton {
263   int val = 0;
264 };
265
266 BENCHMARK(NormalSingleton, n) {
267   for (int i = 0; i < n; ++i) {
268     doNotOptimizeAway(getNormalSingleton());
269   }
270 }
271
272 BENCHMARK_RELATIVE(MeyersSingleton, n) {
273   for (int i = 0; i < n; ++i) {
274     doNotOptimizeAway(getMeyersSingleton());
275   }
276 }
277
278 BENCHMARK_RELATIVE(FollySingleton, n) {
279   SingletonVault benchmark_vault;
280   Singleton<BenchmarkSingleton> benchmark_singleton(
281       nullptr, nullptr, &benchmark_vault);
282   benchmark_vault.registrationComplete();
283
284   for (int i = 0; i < n; ++i) {
285     doNotOptimizeAway(Singleton<BenchmarkSingleton>::get(&benchmark_vault));
286   }
287 }
288
289 int main(int argc, char* argv[]) {
290   testing::InitGoogleTest(&argc, argv);
291   google::InitGoogleLogging(argv[0]);
292   google::ParseCommandLineFlags(&argc, &argv, true);
293
294   SingletonVault::singleton()->registrationComplete();
295
296   auto ret = RUN_ALL_TESTS();
297   if (!ret) {
298     folly::runBenchmarksOnFlag();
299   }
300   return ret;
301 }