Try again: folly::Singleton, a class for managing singletons
authorChip Turner <chip@fb.com>
Fri, 23 May 2014 03:17:14 +0000 (20:17 -0700)
committerChip Turner <chip@fb.com>
Fri, 25 Jul 2014 16:07:19 +0000 (09:07 -0700)
Summary:
Singletons are surprisingly tricky in a codebase where
libraries depend on one another.  folly::Singleton hopes to make this
process more reliable by ensuring object creation happens in a safe
order, that destruction is possible, and that singletons are created
on-demand.

The basic fbcode use intention is to invoke registration completion in
initFacebook, so users need only declare singletons via
Singleton<ClassName> in their .cpp files.

This diff ties the Singletons into the core Init process, but not hhvm
(which will be a separate diff).

Test Plan: runtests

Reviewed By: joelm@fb.com, hans@fb.com

Subscribers: fbcode-common-diffs@, hphp-diffs@, soren, anca, lins, aalexandre, ps, trunkagent, lucian, hannesr, yfeldblum, maxwellsayles

FB internal diff: D1453135

folly/Exception.h
folly/experimental/Singleton.cpp [new file with mode: 0644]
folly/experimental/Singleton.h [new file with mode: 0644]
folly/experimental/test/SingletonTest.cpp [new file with mode: 0644]

index f0547c02545adf2797dba310d5a510a0d914958b..b4f6da1496de77db4c7a0017a0687296ffbaa461 100644 (file)
@@ -109,6 +109,20 @@ void checkFopenErrorExplicit(FILE* fp, int savedErrno, Args&&... args) {
   }
 }
 
+template <typename E, typename V, typename... Args>
+void throwOnFail(V&& value, Args&&... args) {
+  if (!value) {
+    throw E(std::forward<Args>(args)...);
+  }
+}
+
+/**
+ * If cond is not true, raise an exception of type E.  E must have a ctor that
+ * works with const char* (a description of the failure).
+ */
+#define CHECK_THROW(cond, E) \
+  ::folly::throwOnFail<E>((cond), "Check failed: " #cond)
+
 }  // namespace folly
 
 #endif /* FOLLY_EXCEPTION_H_ */
diff --git a/folly/experimental/Singleton.cpp b/folly/experimental/Singleton.cpp
new file mode 100644 (file)
index 0000000..e02b8ca
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/experimental/Singleton.h>
+
+#include <string>
+
+namespace folly {
+
+SingletonVault::~SingletonVault() { destroyInstances(); }
+
+void SingletonVault::destroyInstances() {
+  std::lock_guard<std::mutex> guard(mutex_);
+  CHECK_GE(singletons_.size(), creation_order_.size());
+
+  for (auto type_iter = creation_order_.rbegin();
+       type_iter != creation_order_.rend();
+       ++type_iter) {
+    auto type = *type_iter;
+    auto it = singletons_.find(type);
+    CHECK(it != singletons_.end());
+    auto& entry = it->second;
+    std::lock_guard<std::mutex> entry_guard(entry->mutex_);
+    if (entry->instance.use_count() > 1) {
+      LOG(ERROR) << "Singleton of type " << type.name() << " has a living "
+                 << "reference at destroyInstances time; beware!  Raw pointer "
+                 << "is " << entry->instance.get() << " with use_count of "
+                 << entry->instance.use_count();
+    }
+    entry->instance.reset();
+    entry->state = SingletonEntryState::Dead;
+  }
+
+  creation_order_.clear();
+}
+
+SingletonVault* SingletonVault::singleton() {
+  static SingletonVault vault;
+  return &vault;
+}
+}
diff --git a/folly/experimental/Singleton.h b/folly/experimental/Singleton.h
new file mode 100644 (file)
index 0000000..aa3130f
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// SingletonVault - a library to manage the creation and destruction
+// of interdependent singletons.
+//
+// Basic usage of this class is very simple; suppose you have a class
+// called MyExpensiveService, and you only want to construct one (ie,
+// it's a singleton), but you only want to construct it if it is used.
+//
+// In your .h file:
+// class MyExpensiveService { ... };
+//
+// In your .cpp file:
+// namespace { folly::Singleton<MyExpensiveService> the_singleton; }
+//
+// Code can access it via:
+//
+// MyExpensiveService* instance = Singleton<MyExpensiveService>::get();
+// or
+// std::weak_ptr<MyExpensiveService> instance =
+//     Singleton<MyExpensiveService>::get_weak();
+//
+// The singleton will be created on demand.  If the constructor for
+// MyExpensiveService actually makes use of *another* Singleton, then
+// the right thing will happen -- that other singleton will complete
+// construction before get() returns.  However, in the event of a
+// circular dependency, a runtime error will occur.
+//
+// By default, the singleton instance is constructed via new and
+// deleted via delete, but this is configurable:
+//
+// namespace { folly::Singleton<MyExpensiveService> the_singleton(create,
+//                                                                destroy); }
+//
+// Where create and destroy are functions, Singleton<T>::CreateFunc
+// Singleton<T>::TeardownFunc.
+//
+// What if you need to destroy all of your singletons?  Say, some of
+// your singletons manage threads, but you need to fork?  Or your unit
+// test wants to clean up all global state?  Then you can call
+// SingletonVault::singleton()->destroyInstances(), which invokes the
+// TeardownFunc for each singleton, in the reverse order they were
+// created.  It is your responsibility to ensure your singletons can
+// handle cases where the singletons they depend on go away, however.
+
+#pragma once
+#include <folly/Exception.h>
+
+#include <vector>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <functional>
+#include <typeinfo>
+#include <typeindex>
+
+#include <glog/logging.h>
+
+namespace folly {
+
+// For actual usage, please see the Singleton<T> class at the bottom
+// of this file; that is what you will actually interact with.
+
+// SingletonVault is the class that manages singleton instances.  It
+// is unaware of the underlying types of singletons, and simply
+// manages lifecycles and invokes CreateFunc and TeardownFunc when
+// appropriate.  In general, you won't need to interact with the
+// SingletonVault itself.
+//
+// A vault goes through a few stages of life:
+//
+//   1. Registration phase; singletons can be registered, but no
+//      singleton can be created.
+//   2. registrationComplete() has been called; singletons can no
+//      longer be registered, but they can be created.
+//   3. A vault can return to stage 1 when destroyInstances is called.
+//
+// In general, you don't need to worry about any of the above; just
+// ensure registrationComplete() is called near the top of your main()
+// function, otherwise no singletons can be instantiated.
+class SingletonVault {
+ public:
+  SingletonVault() {};
+  ~SingletonVault();
+
+  typedef std::function<void(void*)> TeardownFunc;
+  typedef std::function<void*(void)> CreateFunc;
+
+  // Register a singleton of a given type with the create and teardown
+  // functions.
+  void registerSingleton(const std::type_info& type,
+                         CreateFunc create,
+                         TeardownFunc teardown) {
+    std::lock_guard<std::mutex> guard(mutex_);
+
+    CHECK_THROW(state_ == SingletonVaultState::Registering, std::logic_error);
+    CHECK_THROW(singletons_.find(type) == singletons_.end(), std::logic_error);
+    auto& entry = singletons_[type];
+    if (!entry) {
+      entry.reset(new SingletonEntry);
+    }
+
+    std::lock_guard<std::mutex> entry_guard(entry->mutex_);
+    CHECK(entry->instance == nullptr);
+    CHECK(create);
+    CHECK(teardown);
+    entry->create = create;
+    entry->teardown = teardown;
+    entry->state = SingletonEntryState::Dead;
+  }
+
+  // Mark registration is complete; no more singletons can be
+  // registered at this point.
+  void registrationComplete() {
+    std::lock_guard<std::mutex> guard(mutex_);
+    CHECK_THROW(state_ == SingletonVaultState::Registering, std::logic_error);
+    state_ = SingletonVaultState::Running;
+  }
+
+  // Destroy all singletons; when complete, the vault can create
+  // singletons once again, or remain dormant.
+  void destroyInstances();
+
+  // Retrieve a singleton from the vault, creating it if necessary.
+  std::shared_ptr<void> get_shared(const std::type_info& type) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    CHECK_THROW(state_ == SingletonVaultState::Running, std::logic_error);
+
+    auto it = singletons_.find(type);
+    if (it == singletons_.end()) {
+      throw std::out_of_range(std::string("non-existent singleton: ") +
+                              type.name());
+    }
+
+    auto& entry = it->second;
+    std::unique_lock<std::mutex> entry_lock(entry->mutex_);
+
+    if (entry->state == SingletonEntryState::BeingBorn) {
+      throw std::out_of_range(std::string("circular singleton dependency: ") +
+                              type.name());
+    }
+
+    if (entry->instance == nullptr) {
+      CHECK(entry->state == SingletonEntryState::Dead);
+      entry->state = SingletonEntryState::BeingBorn;
+
+      entry_lock.unlock();
+      lock.unlock();
+      // Can't use make_shared -- no support for a custom deleter, sadly.
+      auto instance = std::shared_ptr<void>(entry->create(), entry->teardown);
+      lock.lock();
+      entry_lock.lock();
+
+      CHECK(entry->state == SingletonEntryState::BeingBorn);
+      entry->instance = instance;
+      entry->state = SingletonEntryState::Living;
+
+      creation_order_.push_back(type);
+    }
+    CHECK(entry->state == SingletonEntryState::Living);
+
+    return entry->instance;
+  }
+
+  // For testing; how many registered and living singletons we have.
+  size_t registeredSingletonCount() const {
+    std::lock_guard<std::mutex> guard(mutex_);
+    return singletons_.size();
+  }
+
+  size_t livingSingletonCount() const {
+    std::lock_guard<std::mutex> guard(mutex_);
+    size_t ret = 0;
+    for (const auto& p : singletons_) {
+      if (p.second->instance) {
+        ++ret;
+      }
+    }
+
+    return ret;
+  }
+
+  // A well-known vault; you can actually have others, but this is the
+  // default.
+  static SingletonVault* singleton();
+
+ private:
+  // The two stages of life for a vault, as mentioned in the class comment.
+  enum class SingletonVaultState {
+    Registering,
+    Running,
+  };
+
+  // Each singleton in the vault can be in three states: dead
+  // (registered but never created), being born (running the
+  // CreateFunc), and living (CreateFunc returned an instance).
+  enum class SingletonEntryState {
+    Dead,
+    BeingBorn,
+    Living,
+  };
+
+  // An actual instance of a singleton, tracking the instance itself,
+  // its state as described above, and the create and teardown
+  // functions.
+  struct SingletonEntry {
+    std::mutex mutex_;
+    std::shared_ptr<void> instance;
+    CreateFunc create = nullptr;
+    TeardownFunc teardown = nullptr;
+    SingletonEntryState state = SingletonEntryState::Dead;
+
+    SingletonEntry() = default;
+    SingletonEntry(const SingletonEntry&) = delete;
+    SingletonEntry& operator=(const SingletonEntry&) = delete;
+    SingletonEntry& operator=(SingletonEntry&&) = delete;
+    SingletonEntry(SingletonEntry&&) = delete;
+  };
+
+  mutable std::mutex mutex_;
+  typedef std::unique_ptr<SingletonEntry> SingletonEntryPtr;
+  std::unordered_map<std::type_index, SingletonEntryPtr> singletons_;
+  std::vector<std::type_index> creation_order_;
+  SingletonVaultState state_ = SingletonVaultState::Registering;
+};
+
+// This is the wrapper class that most users actually interact with.
+// It allows for simple access to registering and instantiating
+// singletons.  Create instances of this class in the global scope of
+// type Singleton<T> to register your singleton for later access via
+// Singleton<T>::get().
+template <typename T>
+class Singleton {
+ public:
+  typedef std::function<T*(void)> CreateFunc;
+  typedef std::function<void(T*)> TeardownFunc;
+
+  // Generally your program life cycle should be fine with calling
+  // get() repeatedly rather than saving the reference, and then not
+  // call get() during process shutdown.
+  static T* get(SingletonVault* vault = nullptr /* for testing */) {
+    return get_shared(vault).get();
+  }
+
+  // If, however, you do need to hold a reference to the specific
+  // singleton, you can try to do so with a weak_ptr.  Avoid this when
+  // possible but the inability to lock the weak pointer can be a
+  // signal that the vault has been destroyed.
+  static std::weak_ptr<T> get_weak(SingletonVault* vault =
+                                       nullptr /* for testing */) {
+    return std::weak_ptr<T>(get_shared(vault));
+  }
+
+  Singleton(Singleton::CreateFunc c = nullptr,
+            Singleton::TeardownFunc t = nullptr,
+            SingletonVault* vault = nullptr /* for testing */) {
+    if (c == nullptr) {
+      c = []() { return new T; };
+    }
+    SingletonVault::TeardownFunc teardown;
+    if (t == nullptr) {
+      teardown = [](void* v) { delete static_cast<T*>(v); };
+    } else {
+      teardown = [t](void* v) { t(static_cast<T*>(v)); };
+    }
+
+    if (vault == nullptr) {
+      vault = SingletonVault::singleton();
+    }
+
+    vault->registerSingleton(typeid(T), c, teardown);
+  }
+
+ private:
+  // Don't use this function, it's private for a reason!  Using it
+  // would defeat the *entire purpose* of the vault in that we lose
+  // the ability to guarantee that, after a destroyInstances is
+  // called, all instances are, in fact, destroyed.  You should use
+  // weak_ptr if you need to hold a reference to the singleton and
+  // guarantee briefly that it exists.
+  //
+  // Yes, you can just get the weak pointer and lock it, but hopefully
+  // if you have taken the time to read this far, you see why that
+  // would be bad.
+  static std::shared_ptr<T> get_shared(SingletonVault* vault =
+                                           nullptr /* for testing */) {
+    return std::static_pointer_cast<T>(
+        (vault ?: SingletonVault::singleton())->get_shared(typeid(T)));
+  }
+};
+}
diff --git a/folly/experimental/test/SingletonTest.cpp b/folly/experimental/test/SingletonTest.cpp
new file mode 100644 (file)
index 0000000..03b9491
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/experimental/Singleton.h>
+
+#include <folly/Benchmark.h>
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+using namespace folly;
+
+// A simple class that tracks how often instances of the class and
+// subclasses are created, and the ordering.
+struct Watchdog {
+  static std::vector<Watchdog*> creation_order;
+  Watchdog() { creation_order.push_back(this); }
+
+  ~Watchdog() {
+    if (creation_order.back() != this) {
+      throw std::out_of_range("Watchdog destruction order mismatch");
+    }
+    creation_order.pop_back();
+  }
+
+  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);
+}
+
+// Exercise some basic codepaths ensuring registration order and
+// destruction order happen as expected, that instances are created
+// when expected, etc etc.
+TEST(Singleton, BasicUsage) {
+  SingletonVault vault;
+
+  EXPECT_EQ(vault.registeredSingletonCount(), 0);
+  Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault);
+  EXPECT_EQ(vault.registeredSingletonCount(), 1);
+
+  Singleton<ChildWatchdog> child_watchdog_singleton(nullptr, nullptr, &vault);
+  EXPECT_EQ(vault.registeredSingletonCount(), 2);
+
+  vault.registrationComplete();
+
+  Watchdog* s1 = Singleton<Watchdog>::get(&vault);
+  EXPECT_NE(s1, nullptr);
+
+  Watchdog* s2 = Singleton<Watchdog>::get(&vault);
+  EXPECT_NE(s2, nullptr);
+
+  EXPECT_EQ(s1, s2);
+
+  auto s3 = Singleton<ChildWatchdog>::get(&vault);
+  EXPECT_NE(s3, nullptr);
+  EXPECT_NE(s2, s3);
+
+  EXPECT_EQ(vault.registeredSingletonCount(), 2);
+  EXPECT_EQ(vault.livingSingletonCount(), 2);
+
+  vault.destroyInstances();
+  EXPECT_EQ(vault.registeredSingletonCount(), 2);
+  EXPECT_EQ(vault.livingSingletonCount(), 0);
+}
+
+// Some pathological cases such as getting unregistered singletons,
+// double registration, etc.
+TEST(Singleton, NaughtyUsage) {
+  SingletonVault vault;
+  vault.registrationComplete();
+
+  // Unregistered.
+  EXPECT_THROW(Singleton<Watchdog>::get(), std::out_of_range);
+  EXPECT_THROW(Singleton<Watchdog>::get(&vault), std::out_of_range);
+
+  // Registring singletons after registrationComplete called.
+  EXPECT_THROW([&vault]() {
+                 Singleton<Watchdog> watchdog_singleton(
+                     nullptr, nullptr, &vault);
+               }(),
+               std::logic_error);
+
+  EXPECT_THROW([]() { Singleton<Watchdog> watchdog_singleton; }(),
+               std::logic_error);
+
+  SingletonVault vault_2;
+  EXPECT_THROW(Singleton<Watchdog>::get(&vault_2), std::logic_error);
+  Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault_2);
+  // double registration
+  EXPECT_THROW([&vault_2]() {
+                 Singleton<Watchdog> watchdog_singleton(
+                     nullptr, nullptr, &vault_2);
+               }(),
+               std::logic_error);
+  vault_2.destroyInstances();
+  // double registration after destroy
+  EXPECT_THROW([&vault_2]() {
+                 Singleton<Watchdog> watchdog_singleton(
+                     nullptr, nullptr, &vault_2);
+               }(),
+               std::logic_error);
+}
+
+TEST(Singleton, SharedPtrUsage) {
+  SingletonVault vault;
+
+  EXPECT_EQ(vault.registeredSingletonCount(), 0);
+  Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault);
+  EXPECT_EQ(vault.registeredSingletonCount(), 1);
+
+  Singleton<ChildWatchdog> child_watchdog_singleton(nullptr, nullptr, &vault);
+  EXPECT_EQ(vault.registeredSingletonCount(), 2);
+
+  vault.registrationComplete();
+
+  Watchdog* s1 = Singleton<Watchdog>::get(&vault);
+  EXPECT_NE(s1, nullptr);
+
+  Watchdog* s2 = Singleton<Watchdog>::get(&vault);
+  EXPECT_NE(s2, nullptr);
+
+  EXPECT_EQ(s1, s2);
+
+  auto weak_s1 = Singleton<Watchdog>::get_weak(&vault);
+  auto shared_s1 = weak_s1.lock();
+  EXPECT_EQ(shared_s1.get(), s1);
+  EXPECT_EQ(shared_s1.use_count(), 2);
+
+  LOG(ERROR) << "The following log message regarding ref counts is expected";
+  vault.destroyInstances();
+  EXPECT_EQ(vault.registeredSingletonCount(), 2);
+  EXPECT_EQ(vault.livingSingletonCount(), 0);
+
+  EXPECT_EQ(shared_s1.use_count(), 1);
+  EXPECT_EQ(shared_s1.get(), s1);
+
+  auto locked_s1 = weak_s1.lock();
+  EXPECT_EQ(locked_s1.get(), s1);
+  EXPECT_EQ(shared_s1.use_count(), 2);
+  locked_s1.reset();
+  EXPECT_EQ(shared_s1.use_count(), 1);
+  shared_s1.reset();
+  locked_s1 = weak_s1.lock();
+  EXPECT_TRUE(weak_s1.expired());
+
+  Watchdog* new_s1 = Singleton<Watchdog>::get(&vault);
+  EXPECT_NE(new_s1, s1);
+}
+
+// Some classes to test singleton dependencies.  NeedySingleton has a
+// dependency on NeededSingleton, which happens during its
+// construction.
+SingletonVault needy_vault;
+
+struct NeededSingleton {};
+struct NeedySingleton {
+  NeedySingleton() {
+    auto unused = Singleton<NeededSingleton>::get(&needy_vault);
+    EXPECT_NE(unused, nullptr);
+  }
+};
+
+// Ensure circular dependencies fail -- a singleton that needs itself, whoops.
+SingletonVault self_needy_vault;
+struct SelfNeedySingleton {
+  SelfNeedySingleton() {
+    auto unused = Singleton<SelfNeedySingleton>::get(&self_needy_vault);
+    EXPECT_NE(unused, nullptr);
+  }
+};
+
+TEST(Singleton, SingletonDependencies) {
+  Singleton<NeededSingleton> needed_singleton(nullptr, nullptr, &needy_vault);
+  Singleton<NeedySingleton> needy_singleton(nullptr, nullptr, &needy_vault);
+  needy_vault.registrationComplete();
+
+  EXPECT_EQ(needy_vault.registeredSingletonCount(), 2);
+  EXPECT_EQ(needy_vault.livingSingletonCount(), 0);
+
+  auto needy = Singleton<NeedySingleton>::get(&needy_vault);
+  EXPECT_EQ(needy_vault.livingSingletonCount(), 2);
+
+  Singleton<SelfNeedySingleton> self_needy_singleton(
+      nullptr, nullptr, &self_needy_vault);
+  self_needy_vault.registrationComplete();
+  EXPECT_THROW([]() {
+                 Singleton<SelfNeedySingleton>::get(&self_needy_vault);
+               }(),
+               std::out_of_range);
+}
+
+// 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;
+}
+
+int normal_singleton_value = 0;
+int* getNormalSingleton() {
+  doNotOptimizeAway(&normal_singleton_value);
+  return &normal_singleton_value;
+}
+
+struct BenchmarkSingleton {
+  int val = 0;
+};
+
+BENCHMARK(NormalSingleton, n) {
+  for (int i = 0; i < n; ++i) {
+    doNotOptimizeAway(getNormalSingleton());
+  }
+}
+
+BENCHMARK_RELATIVE(MeyersSingleton, n) {
+  for (int i = 0; i < n; ++i) {
+    doNotOptimizeAway(getMeyersSingleton());
+  }
+}
+
+BENCHMARK_RELATIVE(FollySingleton, n) {
+  SingletonVault benchmark_vault;
+  Singleton<BenchmarkSingleton> benchmark_singleton(
+      nullptr, nullptr, &benchmark_vault);
+  benchmark_vault.registrationComplete();
+
+  for (int i = 0; i < n; ++i) {
+    doNotOptimizeAway(Singleton<BenchmarkSingleton>::get(&benchmark_vault));
+  }
+}
+
+int main(int argc, char* argv[]) {
+  testing::InitGoogleTest(&argc, argv);
+  google::InitGoogleLogging(argv[0]);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+
+  SingletonVault::singleton()->registrationComplete();
+
+  auto ret = RUN_ALL_TESTS();
+  if (!ret) {
+    folly::runBenchmarksOnFlag();
+  }
+  return ret;
+}