Remove extra `int main`s from unit tests.
[folly.git] / folly / test / SingletonTest.cpp
index f5d5983a728b1e35e8d17a3bbd4d858039e5b481..b36b47091123d28b265683fc470faf7c506cfac0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <thread>
 
 #include <folly/Singleton.h>
-
-#include <folly/Benchmark.h>
+#include <folly/io/async/EventBase.h>
+#include <folly/test/SingletonTestStructs.h>
 
 #include <glog/logging.h>
 #include <gtest/gtest.h>
+#include <boost/thread/barrier.hpp>
 
 using namespace folly;
 
-// A simple class that tracks how often instances of the class and
-// subclasses are created, and the ordering.  Also tracks a global
-// unique counter for each object.
-std::atomic<size_t> global_counter(19770326);
-struct Watchdog {
-  static std::vector<Watchdog*> creation_order;
-  Watchdog() : serial_number(++global_counter) {
-    creation_order.push_back(this);
-  }
-
-  ~Watchdog() {
-    if (creation_order.back() != this) {
-      throw std::out_of_range("Watchdog destruction order mismatch");
-    }
-    creation_order.pop_back();
-  }
-
-  const size_t serial_number;
-  size_t livingWatchdogCount() const { return creation_order.size(); }
-
-  Watchdog(const Watchdog&) = delete;
-  Watchdog& operator=(const Watchdog&) = delete;
-  Watchdog(Watchdog&&) noexcept = default;
-};
-
-std::vector<Watchdog*> Watchdog::creation_order;
-
-// Some basic types we use for tracking.
-struct ChildWatchdog : public Watchdog {};
-struct GlobalWatchdog : public Watchdog {};
-struct UnregisteredWatchdog : public Watchdog {};
-
-namespace {
-Singleton<GlobalWatchdog> global_watchdog;
-}
-
-// Test basic global usage (the default way singletons will generally
-// be used).
-TEST(Singleton, BasicGlobalUsage) {
-  EXPECT_EQ(Watchdog::creation_order.size(), 0);
-  EXPECT_EQ(SingletonVault::singleton()->registeredSingletonCount(), 1);
-  EXPECT_EQ(SingletonVault::singleton()->livingSingletonCount(), 0);
-  auto wd1 = Singleton<GlobalWatchdog>::get();
-  EXPECT_NE(wd1, nullptr);
-  EXPECT_EQ(Watchdog::creation_order.size(), 1);
-  auto wd2 = Singleton<GlobalWatchdog>::get();
-  EXPECT_NE(wd2, nullptr);
-  EXPECT_EQ(wd1, wd2);
-  EXPECT_EQ(Watchdog::creation_order.size(), 1);
-  SingletonVault::singleton()->destroyInstances();
-  EXPECT_EQ(Watchdog::creation_order.size(), 0);
-}
-
 TEST(Singleton, MissingSingleton) {
-  EXPECT_THROW([]() { auto u = Singleton<UnregisteredWatchdog>::get(); }(),
-               std::out_of_range);
+  EXPECT_DEATH([]() { auto u = Singleton<UnregisteredWatchdog>::try_get(); }(),
+      "");
 }
 
 struct BasicUsageTag {};
@@ -102,20 +50,25 @@ TEST(Singleton, BasicUsage) {
 
   vault.registrationComplete();
 
-  Watchdog* s1 = SingletonBasicUsage<Watchdog>::get();
-  EXPECT_NE(s1, nullptr);
+  // limit a scope to release references so we can destroy them later
+  {
+    std::shared_ptr<Watchdog> s1 = SingletonBasicUsage<Watchdog>::try_get();
+    EXPECT_NE(s1, nullptr);
 
-  Watchdog* s2 = SingletonBasicUsage<Watchdog>::get();
-  EXPECT_NE(s2, nullptr);
+    std::shared_ptr<Watchdog> s2 = SingletonBasicUsage<Watchdog>::try_get();
+    EXPECT_NE(s2, nullptr);
 
-  EXPECT_EQ(s1, s2);
+    EXPECT_EQ(s1, s2);
+    EXPECT_EQ(s1.get(), SingletonBasicUsage<Watchdog>::try_get_fast().get());
 
-  auto s3 = SingletonBasicUsage<ChildWatchdog>::get();
-  EXPECT_NE(s3, nullptr);
-  EXPECT_NE(s2, s3);
+    std::shared_ptr<ChildWatchdog> s3 =
+      SingletonBasicUsage<ChildWatchdog>::try_get();
+    EXPECT_NE(s3, nullptr);
+    EXPECT_NE(s2, s3);
 
-  EXPECT_EQ(vault.registeredSingletonCount(), 2);
-  EXPECT_EQ(vault.livingSingletonCount(), 2);
+    EXPECT_EQ(vault.registeredSingletonCount(), 2);
+    EXPECT_EQ(vault.livingSingletonCount(), 2);
+  }
 
   vault.destroyInstances();
   EXPECT_EQ(vault.registeredSingletonCount(), 2);
@@ -139,11 +92,10 @@ TEST(Singleton, DirectUsage) {
   EXPECT_EQ(vault.registeredSingletonCount(), 2);
   vault.registrationComplete();
 
-  EXPECT_NE(watchdog.get(), nullptr);
-  EXPECT_EQ(watchdog.get(), SingletonDirectUsage<Watchdog>::get());
-  EXPECT_NE(watchdog.get(), named_watchdog.get());
-  EXPECT_EQ(watchdog->livingWatchdogCount(), 2);
-  EXPECT_EQ((*watchdog).livingWatchdogCount(), 2);
+  EXPECT_NE(watchdog.try_get(), nullptr);
+  EXPECT_EQ(watchdog.try_get(), SingletonDirectUsage<Watchdog>::try_get());
+  EXPECT_NE(watchdog.try_get(), named_watchdog.try_get());
+  EXPECT_EQ(watchdog.try_get()->livingWatchdogCount(), 2);
 
   vault.destroyInstances();
 }
@@ -169,22 +121,23 @@ TEST(Singleton, NamedUsage) {
   EXPECT_EQ(vault.registeredSingletonCount(), 3);
 
   vault.registrationComplete();
-
-  // Verify our three singletons are distinct and non-nullptr.
-  Watchdog* s1 = SingletonNamedUsage<Watchdog, Watchdog1>::get();
-  EXPECT_EQ(s1, watchdog1_singleton.get());
-  Watchdog* s2 = SingletonNamedUsage<Watchdog, Watchdog2>::get();
-  EXPECT_EQ(s2, watchdog2_singleton.get());
-  EXPECT_NE(s1, s2);
-  Watchdog* s3 = SingletonNamedUsage<Watchdog, Watchdog3>::get();
-  EXPECT_EQ(s3, watchdog3_singleton.get());
-  EXPECT_NE(s3, s1);
-  EXPECT_NE(s3, s2);
-
-  // Verify the "default" singleton is the same as the DefaultTag-tagged
-  // singleton.
-  Watchdog* s4 = SingletonNamedUsage<Watchdog>::get();
-  EXPECT_EQ(s4, watchdog3_singleton.get());
+  {
+    // Verify our three singletons are distinct and non-nullptr.
+    auto s1 = SingletonNamedUsage<Watchdog, Watchdog1>::try_get();
+    EXPECT_EQ(s1, watchdog1_singleton.try_get());
+    auto s2 = SingletonNamedUsage<Watchdog, Watchdog2>::try_get();
+    EXPECT_EQ(s2, watchdog2_singleton.try_get());
+    EXPECT_NE(s1, s2);
+    auto s3 = SingletonNamedUsage<Watchdog, Watchdog3>::try_get();
+    EXPECT_EQ(s3, watchdog3_singleton.try_get());
+    EXPECT_NE(s3, s1);
+    EXPECT_NE(s3, s2);
+
+    // Verify the "default" singleton is the same as the DefaultTag-tagged
+    // singleton.
+    auto s4 = SingletonNamedUsage<Watchdog>::try_get();
+    EXPECT_EQ(s4, watchdog3_singleton.try_get());
+  }
 
   vault.destroyInstances();
 }
@@ -204,32 +157,31 @@ TEST(Singleton, NaughtyUsage) {
   vault.registrationComplete();
 
   // Unregistered.
-  EXPECT_THROW(Singleton<Watchdog>::get(), std::out_of_range);
-  EXPECT_THROW(SingletonNaughtyUsage<Watchdog>::get(), std::out_of_range);
+  EXPECT_DEATH(Singleton<Watchdog>::try_get(), "");
+  EXPECT_DEATH(SingletonNaughtyUsage<Watchdog>::try_get(), "");
 
   vault.destroyInstances();
 
   auto& vault2 = *SingletonVault::singleton<NaughtyUsageTag2>();
 
-  EXPECT_THROW(SingletonNaughtyUsage2<Watchdog>::get(), std::logic_error);
+   EXPECT_DEATH(SingletonNaughtyUsage2<Watchdog>::try_get(), "");
   SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
+
   // double registration
-  EXPECT_THROW([]() {
-      SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
-    }(),
-    std::logic_error);
+  EXPECT_DEATH([]() { SingletonNaughtyUsage2<Watchdog> watchdog_singleton; }(),
+               "");
   vault2.destroyInstances();
+
   // double registration after destroy
-  EXPECT_THROW([]() {
-      SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
-    }(),
-    std::logic_error);
+  EXPECT_DEATH([]() { SingletonNaughtyUsage2<Watchdog> watchdog_singleton; }(),
+               "");
 }
 
 struct SharedPtrUsageTag {};
 template <typename T, typename Tag = detail::DefaultTag>
 using SingletonSharedPtrUsage = Singleton <T, Tag, SharedPtrUsageTag>;
 
+// TODO (anob): revisit this test
 TEST(Singleton, SharedPtrUsage) {
   struct WatchdogHolder {
     ~WatchdogHolder() {
@@ -259,12 +211,12 @@ TEST(Singleton, SharedPtrUsage) {
 
   // Initilize holder singleton first, so that it's the last one to be
   // destroyed.
-  watchdog_holder_singleton.get();
+  watchdog_holder_singleton.try_get();
 
-  Watchdog* s1 = SingletonSharedPtrUsage<Watchdog>::get();
+  auto s1 = SingletonSharedPtrUsage<Watchdog>::try_get().get();
   EXPECT_NE(s1, nullptr);
 
-  Watchdog* s2 = SingletonSharedPtrUsage<Watchdog>::get();
+  auto s2 = SingletonSharedPtrUsage<Watchdog>::try_get().get();
   EXPECT_NE(s2, nullptr);
 
   EXPECT_EQ(s1, s2);
@@ -286,7 +238,7 @@ TEST(Singleton, SharedPtrUsage) {
 
   // We should release externally locked shared_ptr, otherwise it will be
   // considered a leak
-  watchdog_holder_singleton->watchdog = std::move(shared_s1);
+  watchdog_holder_singleton.try_get()->watchdog = std::move(shared_s1);
 
   LOG(ERROR) << "The following log message regarding shared_ptr is expected";
   {
@@ -306,11 +258,13 @@ TEST(Singleton, SharedPtrUsage) {
 
   vault.reenableInstances();
 
-  // Singleton should be re-created only after reenableInstances() was called.
-  Watchdog* new_s1 = SingletonSharedPtrUsage<Watchdog>::get();
-  // Track serial number rather than pointer since the memory could be
-  // re-used when we create new_s1.
-  EXPECT_NE(new_s1->serial_number, old_serial);
+  {
+    // Singleton should be re-created only after reenableInstances() was called.
+    auto new_s1 = SingletonSharedPtrUsage<Watchdog>::try_get();
+    // Track serial number rather than pointer since the memory could be
+    // re-used when we create new_s1.
+    EXPECT_NE(new_s1->serial_number, old_serial);
+  }
 
   auto new_s1_weak = SingletonSharedPtrUsage<Watchdog>::get_weak();
   auto new_s1_shared = new_s1_weak.lock();
@@ -340,7 +294,7 @@ using SingletonNeedy = Singleton <T, Tag, NeedyTag>;
 struct NeededSingleton {};
 struct NeedySingleton {
   NeedySingleton() {
-    auto unused = SingletonNeedy<NeededSingleton>::get();
+    auto unused = SingletonNeedy<NeededSingleton>::try_get();
     EXPECT_NE(unused, nullptr);
   }
 };
@@ -352,7 +306,7 @@ using SingletonSelfNeedy = Singleton <T, Tag, SelfNeedyTag>;
 
 struct SelfNeedySingleton {
   SelfNeedySingleton() {
-    auto unused = SingletonSelfNeedy<SelfNeedySingleton>::get();
+    auto unused = SingletonSelfNeedy<SelfNeedySingleton>::try_get();
     EXPECT_NE(unused, nullptr);
   }
 };
@@ -367,17 +321,15 @@ TEST(Singleton, SingletonDependencies) {
   EXPECT_EQ(needy_vault.registeredSingletonCount(), 2);
   EXPECT_EQ(needy_vault.livingSingletonCount(), 0);
 
-  auto needy = SingletonNeedy<NeedySingleton>::get();
+  auto needy = SingletonNeedy<NeedySingleton>::try_get();
   EXPECT_EQ(needy_vault.livingSingletonCount(), 2);
 
   SingletonSelfNeedy<SelfNeedySingleton> self_needy_singleton;
   auto& self_needy_vault = *SingletonVault::singleton<SelfNeedyTag>();
 
   self_needy_vault.registrationComplete();
-  EXPECT_THROW([]() {
-      SingletonSelfNeedy<SelfNeedySingleton>::get();
-    }(),
-    std::out_of_range);
+  EXPECT_DEATH([]() { SingletonSelfNeedy<SelfNeedySingleton>::try_get(); }(),
+      "");
 }
 
 // A test to ensure multiple threads contending on singleton creation
@@ -402,7 +354,7 @@ TEST(Singleton, SingletonConcurrency) {
   auto func = [&gatekeeper]() {
     gatekeeper.lock();
     gatekeeper.unlock();
-    auto unused = SingletonConcurrency<Slowpoke>::get();
+    auto unused = SingletonConcurrency<Slowpoke>::try_get();
   };
 
   EXPECT_EQ(vault.livingSingletonCount(), 0);
@@ -420,6 +372,32 @@ TEST(Singleton, SingletonConcurrency) {
   EXPECT_EQ(vault.livingSingletonCount(), 1);
 }
 
+struct ErrorConstructor {
+  static size_t constructCount_;
+  ErrorConstructor() {
+    if ((constructCount_++) == 0) {
+      throw std::runtime_error("first time fails");
+    }
+  }
+};
+size_t ErrorConstructor::constructCount_(0);
+
+struct CreationErrorTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonCreationError = Singleton<T, Tag, CreationErrorTag>;
+
+TEST(Singleton, SingletonCreationError) {
+  SingletonVault::singleton<CreationErrorTag>();
+  SingletonCreationError<ErrorConstructor> error_once_singleton;
+
+  // first time should error out
+  EXPECT_THROW(error_once_singleton.try_get(), std::runtime_error);
+
+  // second time it'll work fine
+  error_once_singleton.try_get();
+  SUCCEED();
+}
+
 struct ConcurrencyStressTag {};
 template <typename T, typename Tag = detail::DefaultTag>
 using SingletonConcurrencyStress = Singleton <T, Tag, ConcurrencyStressTag>;
@@ -431,7 +409,7 @@ TEST(Singleton, SingletonConcurrencyStress) {
   std::vector<std::thread> ts;
   for (size_t i = 0; i < 100; ++i) {
     ts.emplace_back([&]() {
-        slowpoke_singleton.get_weak().lock();
+        slowpoke_singleton.try_get();
       });
   }
 
@@ -449,18 +427,131 @@ TEST(Singleton, SingletonConcurrencyStress) {
   }
 }
 
-// Benchmarking a normal singleton vs a Meyers singleton vs a Folly
-// singleton.  Meyers are insanely fast, but (hopefully) Folly
-// singletons are fast "enough."
-int* getMeyersSingleton() {
-  static auto ret = new int(0);
-  return ret;
+namespace {
+struct EagerInitSyncTag {};
+}
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonEagerInitSync = Singleton<T, Tag, EagerInitSyncTag>;
+TEST(Singleton, SingletonEagerInitSync) {
+  auto& vault = *SingletonVault::singleton<EagerInitSyncTag>();
+  bool didEagerInit = false;
+  auto sing = SingletonEagerInitSync<std::string>(
+                  [&] {didEagerInit = true; return new std::string("foo"); })
+              .shouldEagerInit();
+  vault.registrationComplete();
+  EXPECT_FALSE(didEagerInit);
+  vault.doEagerInit();
+  EXPECT_TRUE(didEagerInit);
+  sing.get_weak();  // (avoid compile error complaining about unused var 'sing')
+}
+
+namespace {
+struct EagerInitAsyncTag {};
+}
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonEagerInitAsync = Singleton<T, Tag, EagerInitAsyncTag>;
+TEST(Singleton, SingletonEagerInitAsync) {
+  auto& vault = *SingletonVault::singleton<EagerInitAsyncTag>();
+  bool didEagerInit = false;
+  auto sing = SingletonEagerInitAsync<std::string>(
+                  [&] {didEagerInit = true; return new std::string("foo"); })
+              .shouldEagerInit();
+  folly::EventBase eb;
+  folly::Baton<> done;
+  vault.registrationComplete();
+  EXPECT_FALSE(didEagerInit);
+  vault.doEagerInitVia(eb, &done);
+  eb.loop();
+  done.wait();
+  EXPECT_TRUE(didEagerInit);
+  sing.get_weak();  // (avoid compile error complaining about unused var 'sing')
+}
+
+namespace {
+class TestEagerInitParallelExecutor : public folly::Executor {
+ public:
+  explicit TestEagerInitParallelExecutor(const size_t threadCount) {
+    eventBases_.reserve(threadCount);
+    threads_.reserve(threadCount);
+    for (size_t i = 0; i < threadCount; i++) {
+      eventBases_.push_back(std::make_shared<folly::EventBase>());
+      auto eb = eventBases_.back();
+      threads_.emplace_back(std::make_shared<std::thread>(
+          [eb] { eb->loopForever(); }));
+    }
+  }
+
+  virtual ~TestEagerInitParallelExecutor() override {
+    for (auto eb : eventBases_) {
+      eb->runInEventBaseThread([eb] { eb->terminateLoopSoon(); });
+    }
+    for (auto thread : threads_) {
+      thread->join();
+    }
+  }
+
+  virtual void add(folly::Func func) override {
+    const auto index = (counter_ ++) % eventBases_.size();
+    eventBases_[index]->add(func);
+  }
+
+ private:
+  std::vector<std::shared_ptr<folly::EventBase>> eventBases_;
+  std::vector<std::shared_ptr<std::thread>> threads_;
+  std::atomic<size_t> counter_ {0};
+};
+}  // namespace
+
+namespace {
+struct EagerInitParallelTag {};
 }
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonEagerInitParallel = Singleton<T, Tag, EagerInitParallelTag>;
+TEST(Singleton, SingletonEagerInitParallel) {
+  const static size_t kIters = 1000;
+  const static size_t kThreads = 20;
+
+  std::atomic<size_t> initCounter;
+
+  auto& vault = *SingletonVault::singleton<EagerInitParallelTag>();
+
+  auto sing = SingletonEagerInitParallel<std::string>(
+                  [&] {++initCounter; return new std::string(""); })
+              .shouldEagerInit();
+
+  for (size_t i = 0; i < kIters; i++) {
+    SCOPE_EXIT {
+      // clean up each time
+      vault.destroyInstances();
+      vault.reenableInstances();
+    };
+
+    initCounter.store(0);
+
+    {
+      std::vector<std::shared_ptr<std::thread>> threads;
+      boost::barrier barrier(kThreads);
+      TestEagerInitParallelExecutor exe(kThreads);
+      vault.registrationComplete();
+
+      EXPECT_EQ(0, initCounter.load());
+
+      for (size_t j = 0; j < kThreads; j++) {
+        threads.push_back(std::make_shared<std::thread>([&] {
+          barrier.wait();
+          vault.doEagerInitVia(exe);
+        }));
+      }
+
+      for (auto thread : threads) {
+        thread->join();
+      }
+    }
+
+    EXPECT_EQ(1, initCounter.load());
 
-int normal_singleton_value = 0;
-int* getNormalSingleton() {
-  doNotOptimizeAway(&normal_singleton_value);
-  return &normal_singleton_value;
+    sing.get_weak();  // (avoid compile error complaining about unused var)
+  }
 }
 
 struct MockTag {};
@@ -478,66 +569,14 @@ TEST(Singleton, MockTest) {
   // Registring singletons after registrationComplete called works
   // with make_mock (but not with Singleton ctor).
   EXPECT_EQ(vault.registeredSingletonCount(), 1);
-  int serial_count_first = SingletonMock<Watchdog>::get()->serial_number;
+  int serial_count_first = SingletonMock<Watchdog>::try_get()->serial_number;
 
   // Override existing mock using make_mock.
   SingletonMock<Watchdog>::make_mock();
 
   EXPECT_EQ(vault.registeredSingletonCount(), 1);
-  int serial_count_mock = SingletonMock<Watchdog>::get()->serial_number;
+  int serial_count_mock = SingletonMock<Watchdog>::try_get()->serial_number;
 
   // If serial_count value is the same, then singleton was not replaced.
   EXPECT_NE(serial_count_first, serial_count_mock);
 }
-
-struct BenchmarkSingleton {
-  int val = 0;
-};
-
-BENCHMARK(NormalSingleton, n) {
-  for (size_t i = 0; i < n; ++i) {
-    doNotOptimizeAway(getNormalSingleton());
-  }
-}
-
-BENCHMARK_RELATIVE(MeyersSingleton, n) {
-  for (size_t i = 0; i < n; ++i) {
-    doNotOptimizeAway(getMeyersSingleton());
-  }
-}
-
-struct BenchmarkTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonBenchmark = Singleton <T, Tag, BenchmarkTag>;
-
-struct GetTag{};
-struct GetWeakTag{};
-
-SingletonBenchmark<BenchmarkSingleton, GetTag> benchmark_singleton_get;
-SingletonBenchmark<BenchmarkSingleton, GetWeakTag> benchmark_singleton_get_weak;
-
-BENCHMARK_RELATIVE(FollySingleton, n) {
-  for (size_t i = 0; i < n; ++i) {
-    doNotOptimizeAway(SingletonBenchmark<BenchmarkSingleton, GetTag>::get());
-  }
-}
-
-BENCHMARK_RELATIVE(FollySingletonWeak, n) {
-  for (size_t i = 0; i < n; ++i) {
-    SingletonBenchmark<BenchmarkSingleton, GetWeakTag>::get_weak();
-  }
-}
-
-int main(int argc, char* argv[]) {
-  testing::InitGoogleTest(&argc, argv);
-  google::InitGoogleLogging(argv[0]);
-  gflags::ParseCommandLineFlags(&argc, &argv, true);
-
-  SingletonVault::singleton()->registrationComplete();
-
-  auto ret = RUN_ALL_TESTS();
-  if (!ret) {
-    folly::runBenchmarksOnFlag();
-  }
-  return ret;
-}