Make Observer.Stress test not fail under load
[folly.git] / folly / test / SingletonTest.cpp
1 /*
2  * Copyright 2016 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 <thread>
18
19 #include <folly/Singleton.h>
20 #include <folly/Subprocess.h>
21 #include <folly/experimental/io/FsUtil.h>
22 #include <folly/io/async/EventBase.h>
23 #include <folly/portability/GMock.h>
24 #include <folly/portability/GTest.h>
25 #include <folly/test/SingletonTestStructs.h>
26
27 #include <glog/logging.h>
28 #include <boost/thread/barrier.hpp>
29
30 using namespace folly;
31
32 TEST(Singleton, MissingSingleton) {
33   EXPECT_DEATH([]() { auto u = Singleton<UnregisteredWatchdog>::try_get(); }(),
34       "");
35 }
36
37 struct BasicUsageTag {};
38 template <typename T, typename Tag = detail::DefaultTag>
39 using SingletonBasicUsage = Singleton <T, Tag, BasicUsageTag>;
40
41 // Exercise some basic codepaths ensuring registration order and
42 // destruction order happen as expected, that instances are created
43 // when expected, etc etc.
44 TEST(Singleton, BasicUsage) {
45   auto& vault = *SingletonVault::singleton<BasicUsageTag>();
46
47   EXPECT_EQ(vault.registeredSingletonCount(), 0);
48   SingletonBasicUsage<Watchdog> watchdog_singleton;
49   EXPECT_EQ(vault.registeredSingletonCount(), 1);
50
51   SingletonBasicUsage<ChildWatchdog> child_watchdog_singleton;
52   EXPECT_EQ(vault.registeredSingletonCount(), 2);
53
54   vault.registrationComplete();
55
56   // limit a scope to release references so we can destroy them later
57   {
58     std::shared_ptr<Watchdog> s1 = SingletonBasicUsage<Watchdog>::try_get();
59     EXPECT_NE(s1, nullptr);
60
61     std::shared_ptr<Watchdog> s2 = SingletonBasicUsage<Watchdog>::try_get();
62     EXPECT_NE(s2, nullptr);
63
64     EXPECT_EQ(s1, s2);
65     EXPECT_EQ(s1.get(), SingletonBasicUsage<Watchdog>::try_get_fast().get());
66
67     std::shared_ptr<ChildWatchdog> s3 =
68       SingletonBasicUsage<ChildWatchdog>::try_get();
69     EXPECT_NE(s3, nullptr);
70     EXPECT_NE(s2, s3);
71
72     EXPECT_EQ(vault.registeredSingletonCount(), 2);
73     EXPECT_EQ(vault.livingSingletonCount(), 2);
74   }
75
76   vault.destroyInstances();
77   EXPECT_EQ(vault.registeredSingletonCount(), 2);
78   EXPECT_EQ(vault.livingSingletonCount(), 0);
79 }
80
81 struct DirectUsageTag {};
82 template <typename T, typename Tag = detail::DefaultTag>
83 using SingletonDirectUsage = Singleton <T, Tag, DirectUsageTag>;
84
85 TEST(Singleton, DirectUsage) {
86   auto& vault = *SingletonVault::singleton<DirectUsageTag>();
87
88   EXPECT_EQ(vault.registeredSingletonCount(), 0);
89
90   // Verify we can get to the underlying singletons via directly using
91   // the singleton definition.
92   SingletonDirectUsage<Watchdog> watchdog;
93   struct TestTag {};
94   SingletonDirectUsage<Watchdog, TestTag> named_watchdog;
95   EXPECT_EQ(vault.registeredSingletonCount(), 2);
96   vault.registrationComplete();
97
98   EXPECT_NE(watchdog.try_get(), nullptr);
99   EXPECT_EQ(watchdog.try_get(), SingletonDirectUsage<Watchdog>::try_get());
100   EXPECT_NE(watchdog.try_get(), named_watchdog.try_get());
101   EXPECT_EQ(watchdog.try_get()->livingWatchdogCount(), 2);
102
103   vault.destroyInstances();
104 }
105
106 struct NamedUsageTag {};
107 template <typename T, typename Tag = detail::DefaultTag>
108 using SingletonNamedUsage = Singleton <T, Tag, NamedUsageTag>;
109
110 TEST(Singleton, NamedUsage) {
111   auto& vault = *SingletonVault::singleton<NamedUsageTag>();
112
113   EXPECT_EQ(vault.registeredSingletonCount(), 0);
114
115   // Define two named Watchdog singletons and one unnamed singleton.
116   struct Watchdog1 {};
117   struct Watchdog2 {};
118   typedef detail::DefaultTag Watchdog3;
119   SingletonNamedUsage<Watchdog, Watchdog1> watchdog1_singleton;
120   EXPECT_EQ(vault.registeredSingletonCount(), 1);
121   SingletonNamedUsage<Watchdog, Watchdog2> watchdog2_singleton;
122   EXPECT_EQ(vault.registeredSingletonCount(), 2);
123   SingletonNamedUsage<Watchdog, Watchdog3> watchdog3_singleton;
124   EXPECT_EQ(vault.registeredSingletonCount(), 3);
125
126   vault.registrationComplete();
127   {
128     // Verify our three singletons are distinct and non-nullptr.
129     auto s1 = SingletonNamedUsage<Watchdog, Watchdog1>::try_get();
130     EXPECT_EQ(s1, watchdog1_singleton.try_get());
131     auto s2 = SingletonNamedUsage<Watchdog, Watchdog2>::try_get();
132     EXPECT_EQ(s2, watchdog2_singleton.try_get());
133     EXPECT_NE(s1, s2);
134     auto s3 = SingletonNamedUsage<Watchdog, Watchdog3>::try_get();
135     EXPECT_EQ(s3, watchdog3_singleton.try_get());
136     EXPECT_NE(s3, s1);
137     EXPECT_NE(s3, s2);
138
139     // Verify the "default" singleton is the same as the DefaultTag-tagged
140     // singleton.
141     auto s4 = SingletonNamedUsage<Watchdog>::try_get();
142     EXPECT_EQ(s4, watchdog3_singleton.try_get());
143   }
144
145   vault.destroyInstances();
146 }
147
148 struct NaughtyUsageTag {};
149 template <typename T, typename Tag = detail::DefaultTag>
150 using SingletonNaughtyUsage = Singleton <T, Tag, NaughtyUsageTag>;
151 struct NaughtyUsageTag2 {};
152 template <typename T, typename Tag = detail::DefaultTag>
153 using SingletonNaughtyUsage2 = Singleton <T, Tag, NaughtyUsageTag2>;
154
155 // Some pathological cases such as getting unregistered singletons,
156 // double registration, etc.
157 TEST(Singleton, NaughtyUsage) {
158   auto& vault = *SingletonVault::singleton<NaughtyUsageTag>();
159
160   vault.registrationComplete();
161
162   // Unregistered.
163   EXPECT_DEATH(Singleton<Watchdog>::try_get(), "");
164   EXPECT_DEATH(SingletonNaughtyUsage<Watchdog>::try_get(), "");
165
166   vault.destroyInstances();
167
168   auto& vault2 = *SingletonVault::singleton<NaughtyUsageTag2>();
169
170    EXPECT_DEATH(SingletonNaughtyUsage2<Watchdog>::try_get(), "");
171   SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
172
173   // double registration
174   EXPECT_DEATH([]() { SingletonNaughtyUsage2<Watchdog> w2; }(), "");
175   vault2.destroyInstances();
176
177   // double registration after destroy
178   EXPECT_DEATH([]() { SingletonNaughtyUsage2<Watchdog> w3; }(), "");
179 }
180
181 struct SharedPtrUsageTag {};
182 template <typename T, typename Tag = detail::DefaultTag>
183 using SingletonSharedPtrUsage = Singleton <T, Tag, SharedPtrUsageTag>;
184
185 // TODO (anob): revisit this test
186 TEST(Singleton, SharedPtrUsage) {
187   struct WatchdogHolder {
188     ~WatchdogHolder() {
189       if (watchdog) {
190         LOG(ERROR) << "The following log message with stack trace is expected";
191       }
192     }
193
194     std::shared_ptr<Watchdog> watchdog;
195   };
196
197   auto& vault = *SingletonVault::singleton<SharedPtrUsageTag>();
198
199   EXPECT_EQ(vault.registeredSingletonCount(), 0);
200   SingletonSharedPtrUsage<Watchdog> watchdog_singleton;
201   EXPECT_EQ(vault.registeredSingletonCount(), 1);
202
203   SingletonSharedPtrUsage<ChildWatchdog> child_watchdog_singleton;
204   EXPECT_EQ(vault.registeredSingletonCount(), 2);
205
206   struct ATag {};
207   SingletonSharedPtrUsage<Watchdog, ATag> named_watchdog_singleton;
208
209   SingletonSharedPtrUsage<WatchdogHolder> watchdog_holder_singleton;
210
211   vault.registrationComplete();
212
213   // Initilize holder singleton first, so that it's the last one to be
214   // destroyed.
215   watchdog_holder_singleton.try_get();
216
217   auto s1 = SingletonSharedPtrUsage<Watchdog>::try_get().get();
218   EXPECT_NE(s1, nullptr);
219
220   auto s2 = SingletonSharedPtrUsage<Watchdog>::try_get().get();
221   EXPECT_NE(s2, nullptr);
222
223   EXPECT_EQ(s1, s2);
224
225   auto weak_s1 = SingletonSharedPtrUsage<Watchdog>::get_weak();
226
227   auto shared_s1 = weak_s1.lock();
228   EXPECT_EQ(shared_s1.get(), s1);
229   EXPECT_EQ(shared_s1.use_count(), 2);
230
231   auto old_serial = shared_s1->serial_number;
232
233   {
234     auto named_weak_s1 =
235       SingletonSharedPtrUsage<Watchdog, ATag>::get_weak();
236     auto locked = named_weak_s1.lock();
237     EXPECT_NE(locked.get(), shared_s1.get());
238   }
239
240   // We should release externally locked shared_ptr, otherwise it will be
241   // considered a leak
242   watchdog_holder_singleton.try_get()->watchdog = std::move(shared_s1);
243
244   LOG(ERROR) << "The following log message regarding shared_ptr is expected";
245   {
246     auto start_time = std::chrono::steady_clock::now();
247     vault.destroyInstances();
248     auto duration = std::chrono::steady_clock::now() - start_time;
249     EXPECT_TRUE(duration > std::chrono::seconds{4} &&
250                 duration < std::chrono::seconds{6});
251   }
252   EXPECT_EQ(vault.registeredSingletonCount(), 4);
253   EXPECT_EQ(vault.livingSingletonCount(), 0);
254
255   EXPECT_TRUE(weak_s1.expired());
256
257   auto empty_s1 = SingletonSharedPtrUsage<Watchdog>::get_weak();
258   EXPECT_FALSE(empty_s1.lock());
259
260   vault.reenableInstances();
261
262   {
263     // Singleton should be re-created only after reenableInstances() was called.
264     auto new_s1 = SingletonSharedPtrUsage<Watchdog>::try_get();
265     // Track serial number rather than pointer since the memory could be
266     // re-used when we create new_s1.
267     EXPECT_NE(new_s1->serial_number, old_serial);
268   }
269
270   auto new_s1_weak = SingletonSharedPtrUsage<Watchdog>::get_weak();
271   auto new_s1_shared = new_s1_weak.lock();
272   std::thread t([new_s1_shared]() mutable {
273       std::this_thread::sleep_for(std::chrono::seconds{2});
274       new_s1_shared.reset();
275     });
276   new_s1_shared.reset();
277   {
278     auto start_time = std::chrono::steady_clock::now();
279     vault.destroyInstances();
280     auto duration = std::chrono::steady_clock::now() - start_time;
281     EXPECT_TRUE(duration > std::chrono::seconds{1} &&
282                 duration < std::chrono::seconds{3});
283   }
284   EXPECT_TRUE(new_s1_weak.expired());
285   t.join();
286 }
287
288 // Some classes to test singleton dependencies.  NeedySingleton has a
289 // dependency on NeededSingleton, which happens during its
290 // construction.
291 struct NeedyTag {};
292 template <typename T, typename Tag = detail::DefaultTag>
293 using SingletonNeedy = Singleton <T, Tag, NeedyTag>;
294
295 struct NeededSingleton {};
296 struct NeedySingleton {
297   NeedySingleton() {
298     auto unused = SingletonNeedy<NeededSingleton>::try_get();
299     EXPECT_NE(unused, nullptr);
300   }
301 };
302
303 // Ensure circular dependencies fail -- a singleton that needs itself, whoops.
304 struct SelfNeedyTag {};
305 template <typename T, typename Tag = detail::DefaultTag>
306 using SingletonSelfNeedy = Singleton <T, Tag, SelfNeedyTag>;
307
308 struct SelfNeedySingleton {
309   SelfNeedySingleton() {
310     auto unused = SingletonSelfNeedy<SelfNeedySingleton>::try_get();
311     EXPECT_NE(unused, nullptr);
312   }
313 };
314
315 TEST(Singleton, SingletonDependencies) {
316   SingletonNeedy<NeededSingleton> needed_singleton;
317   SingletonNeedy<NeedySingleton> needy_singleton;
318   auto& needy_vault = *SingletonVault::singleton<NeedyTag>();
319
320   needy_vault.registrationComplete();
321
322   EXPECT_EQ(needy_vault.registeredSingletonCount(), 2);
323   EXPECT_EQ(needy_vault.livingSingletonCount(), 0);
324
325   auto needy = SingletonNeedy<NeedySingleton>::try_get();
326   EXPECT_EQ(needy_vault.livingSingletonCount(), 2);
327
328   SingletonSelfNeedy<SelfNeedySingleton> self_needy_singleton;
329   auto& self_needy_vault = *SingletonVault::singleton<SelfNeedyTag>();
330
331   self_needy_vault.registrationComplete();
332   EXPECT_DEATH([]() { SingletonSelfNeedy<SelfNeedySingleton>::try_get(); }(),
333       "");
334 }
335
336 // A test to ensure multiple threads contending on singleton creation
337 // properly wait for creation rather than thinking it is a circular
338 // dependency.
339 class Slowpoke : public Watchdog {
340  public:
341   Slowpoke() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); }
342 };
343
344 struct ConcurrencyTag {};
345 template <typename T, typename Tag = detail::DefaultTag>
346 using SingletonConcurrency = Singleton <T, Tag, ConcurrencyTag>;
347
348 TEST(Singleton, SingletonConcurrency) {
349   auto& vault = *SingletonVault::singleton<ConcurrencyTag>();
350   SingletonConcurrency<Slowpoke> slowpoke_singleton;
351   vault.registrationComplete();
352
353   std::mutex gatekeeper;
354   gatekeeper.lock();
355   auto func = [&gatekeeper]() {
356     gatekeeper.lock();
357     gatekeeper.unlock();
358     auto unused = SingletonConcurrency<Slowpoke>::try_get();
359   };
360
361   EXPECT_EQ(vault.livingSingletonCount(), 0);
362   std::vector<std::thread> threads;
363   for (int i = 0; i < 100; ++i) {
364     threads.emplace_back(func);
365   }
366   // If circular dependency checks fail, the unlock would trigger a
367   // crash.  Instead, it succeeds, and we have exactly one living
368   // singleton.
369   gatekeeper.unlock();
370   for (auto& t : threads) {
371     t.join();
372   }
373   EXPECT_EQ(vault.livingSingletonCount(), 1);
374 }
375
376 struct ErrorConstructor {
377   static size_t constructCount_;
378   ErrorConstructor() {
379     if ((constructCount_++) == 0) {
380       throw std::runtime_error("first time fails");
381     }
382   }
383 };
384 size_t ErrorConstructor::constructCount_(0);
385
386 struct CreationErrorTag {};
387 template <typename T, typename Tag = detail::DefaultTag>
388 using SingletonCreationError = Singleton<T, Tag, CreationErrorTag>;
389
390 TEST(Singleton, SingletonCreationError) {
391   SingletonVault::singleton<CreationErrorTag>();
392   SingletonCreationError<ErrorConstructor> error_once_singleton;
393
394   // first time should error out
395   EXPECT_THROW(error_once_singleton.try_get(), std::runtime_error);
396
397   // second time it'll work fine
398   error_once_singleton.try_get();
399   SUCCEED();
400 }
401
402 struct ConcurrencyStressTag {};
403 template <typename T, typename Tag = detail::DefaultTag>
404 using SingletonConcurrencyStress = Singleton <T, Tag, ConcurrencyStressTag>;
405
406 TEST(Singleton, SingletonConcurrencyStress) {
407   auto& vault = *SingletonVault::singleton<ConcurrencyStressTag>();
408   SingletonConcurrencyStress<Slowpoke> slowpoke_singleton;
409
410   std::vector<std::thread> ts;
411   for (size_t i = 0; i < 100; ++i) {
412     ts.emplace_back([&]() {
413         slowpoke_singleton.try_get();
414       });
415   }
416
417   for (size_t i = 0; i < 100; ++i) {
418     std::chrono::milliseconds d(20);
419
420     std::this_thread::sleep_for(d);
421     vault.destroyInstances();
422     std::this_thread::sleep_for(d);
423     vault.destroyInstances();
424   }
425
426   for (auto& t : ts) {
427     t.join();
428   }
429 }
430
431 namespace {
432 struct EagerInitSyncTag {};
433 }
434 template <typename T, typename Tag = detail::DefaultTag>
435 using SingletonEagerInitSync = Singleton<T, Tag, EagerInitSyncTag>;
436 TEST(Singleton, SingletonEagerInitSync) {
437   auto& vault = *SingletonVault::singleton<EagerInitSyncTag>();
438   bool didEagerInit = false;
439   auto sing = SingletonEagerInitSync<std::string>(
440                   [&] {didEagerInit = true; return new std::string("foo"); })
441               .shouldEagerInit();
442   vault.registrationComplete();
443   EXPECT_FALSE(didEagerInit);
444   vault.doEagerInit();
445   EXPECT_TRUE(didEagerInit);
446   sing.get_weak();  // (avoid compile error complaining about unused var 'sing')
447 }
448
449 namespace {
450 struct EagerInitAsyncTag {};
451 }
452 template <typename T, typename Tag = detail::DefaultTag>
453 using SingletonEagerInitAsync = Singleton<T, Tag, EagerInitAsyncTag>;
454 TEST(Singleton, SingletonEagerInitAsync) {
455   auto& vault = *SingletonVault::singleton<EagerInitAsyncTag>();
456   bool didEagerInit = false;
457   auto sing = SingletonEagerInitAsync<std::string>(
458                   [&] {didEagerInit = true; return new std::string("foo"); })
459               .shouldEagerInit();
460   folly::EventBase eb;
461   folly::Baton<> done;
462   vault.registrationComplete();
463   EXPECT_FALSE(didEagerInit);
464   vault.doEagerInitVia(eb, &done);
465   eb.loop();
466   done.wait();
467   EXPECT_TRUE(didEagerInit);
468   sing.get_weak();  // (avoid compile error complaining about unused var 'sing')
469 }
470
471 namespace {
472 class TestEagerInitParallelExecutor : public folly::Executor {
473  public:
474   explicit TestEagerInitParallelExecutor(const size_t threadCount) {
475     eventBases_.reserve(threadCount);
476     threads_.reserve(threadCount);
477     for (size_t i = 0; i < threadCount; i++) {
478       eventBases_.push_back(std::make_shared<folly::EventBase>());
479       auto eb = eventBases_.back();
480       threads_.emplace_back(std::make_shared<std::thread>(
481           [eb] { eb->loopForever(); }));
482     }
483   }
484
485   virtual ~TestEagerInitParallelExecutor() override {
486     for (auto eb : eventBases_) {
487       eb->runInEventBaseThread([eb] { eb->terminateLoopSoon(); });
488     }
489     for (auto thread : threads_) {
490       thread->join();
491     }
492   }
493
494   virtual void add(folly::Func func) override {
495     const auto index = (counter_ ++) % eventBases_.size();
496     eventBases_[index]->add(std::move(func));
497   }
498
499  private:
500   std::vector<std::shared_ptr<folly::EventBase>> eventBases_;
501   std::vector<std::shared_ptr<std::thread>> threads_;
502   std::atomic<size_t> counter_ {0};
503 };
504 }  // namespace
505
506 namespace {
507 struct EagerInitParallelTag {};
508 }
509 template <typename T, typename Tag = detail::DefaultTag>
510 using SingletonEagerInitParallel = Singleton<T, Tag, EagerInitParallelTag>;
511 TEST(Singleton, SingletonEagerInitParallel) {
512   const static size_t kIters = 1000;
513   const static size_t kThreads = 20;
514
515   std::atomic<size_t> initCounter;
516
517   auto& vault = *SingletonVault::singleton<EagerInitParallelTag>();
518
519   auto sing = SingletonEagerInitParallel<std::string>(
520                   [&] {++initCounter; return new std::string(""); })
521               .shouldEagerInit();
522
523   for (size_t i = 0; i < kIters; i++) {
524     SCOPE_EXIT {
525       // clean up each time
526       vault.destroyInstances();
527       vault.reenableInstances();
528     };
529
530     initCounter.store(0);
531
532     {
533       std::vector<std::shared_ptr<std::thread>> threads;
534       boost::barrier barrier(kThreads);
535       TestEagerInitParallelExecutor exe(kThreads);
536       vault.registrationComplete();
537
538       EXPECT_EQ(0, initCounter.load());
539
540       for (size_t j = 0; j < kThreads; j++) {
541         threads.push_back(std::make_shared<std::thread>([&] {
542           barrier.wait();
543           vault.doEagerInitVia(exe);
544         }));
545       }
546
547       for (auto thread : threads) {
548         thread->join();
549       }
550     }
551
552     EXPECT_EQ(1, initCounter.load());
553
554     sing.get_weak();  // (avoid compile error complaining about unused var)
555   }
556 }
557
558 struct MockTag {};
559 template <typename T, typename Tag = detail::DefaultTag>
560 using SingletonMock = Singleton <T, Tag, MockTag>;
561
562 // Verify that existing Singleton's can be overridden
563 // using the make_mock functionality.
564 TEST(Singleton, MockTest) {
565   auto& vault = *SingletonVault::singleton<MockTag>();
566
567   SingletonMock<Watchdog> watchdog_singleton;
568   vault.registrationComplete();
569
570   // Registring singletons after registrationComplete called works
571   // with make_mock (but not with Singleton ctor).
572   EXPECT_EQ(vault.registeredSingletonCount(), 1);
573   int serial_count_first = SingletonMock<Watchdog>::try_get()->serial_number;
574
575   // Override existing mock using make_mock.
576   SingletonMock<Watchdog>::make_mock();
577
578   EXPECT_EQ(vault.registeredSingletonCount(), 1);
579   int serial_count_mock = SingletonMock<Watchdog>::try_get()->serial_number;
580
581   // If serial_count value is the same, then singleton was not replaced.
582   EXPECT_NE(serial_count_first, serial_count_mock);
583
584   // Override existing mock using make_mock one more time
585   SingletonMock<Watchdog>::make_mock();
586
587   EXPECT_EQ(vault.registeredSingletonCount(), 1);
588   int serial_count_mock2 = SingletonMock<Watchdog>::try_get()->serial_number;
589
590   // If serial_count value is the same, then singleton was not replaced.
591   EXPECT_NE(serial_count_first, serial_count_mock2);
592   EXPECT_NE(serial_count_mock, serial_count_mock2);
593
594   vault.destroyInstances();
595 }
596
597 TEST(Singleton, DoubleRegistrationLogging) {
598   const auto basename = "singleton_double_registration";
599   const auto sub = fs::executable_path().remove_filename() / basename;
600   auto p = Subprocess(
601       std::vector<std::string>{sub.string()},
602       Subprocess::Options()
603           .stdin(Subprocess::CLOSE)
604           .stdout(Subprocess::CLOSE)
605           .pipeStderr()
606           .closeOtherFds());
607   auto err = p.communicate("").second;
608   auto res = p.wait();
609   EXPECT_EQ(ProcessReturnCode::KILLED, res.state());
610   EXPECT_EQ(SIGABRT, res.killSignal());
611   EXPECT_THAT(err, testing::StartsWith("Double registration of singletons"));
612 }
613
614 // Singleton using a non default constructor test/example:
615 struct X {
616   X() : X(-1, "unset") {}
617   X(int a1, std::string a2) : a1(a1), a2(a2) {
618     LOG(INFO) << "X(" << a1 << "," << a2 << ")";
619   }
620   const int a1;
621   const std::string a2;
622 };
623
624 folly::Singleton<X> singleton_x([]() { return new X(42, "foo"); });
625
626 TEST(Singleton, CustomCreator) {
627   X x1;
628   std::shared_ptr<X> x2p = singleton_x.try_get();
629   EXPECT_NE(nullptr, x2p);
630   EXPECT_NE(x1.a1, x2p->a1);
631   EXPECT_NE(x1.a2, x2p->a2);
632   EXPECT_EQ(42, x2p->a1);
633   EXPECT_EQ(std::string("foo"), x2p->a2);
634 }