Add non-strict mode to folly::Singleton
authorChip Turner <chip@fb.com>
Fri, 12 Sep 2014 00:36:50 +0000 (17:36 -0700)
committerDave Watson <davejwatson@fb.com>
Mon, 15 Sep 2014 21:30:20 +0000 (14:30 -0700)
Summary:
It is difficult to guarantee every binary can call
registrationComplete; for now, support a mode where we don't enforce the
strict registration lifecycle.  In the fullness of time, we can make
strictness the default.

Test Plan: runtests

Reviewed By: lins@fb.com

Subscribers: lins, anca, njormrod

FB internal diff: D1551688

folly/experimental/Singleton.h
folly/experimental/test/SingletonTest.cpp

index c861e1bc18cdb11110c401facf1bcf9ff74660df..73625a99eb02751ca0994817f48dc076423c336e 100644 (file)
@@ -163,7 +163,9 @@ class TypeDescriptorHasher {
 
 class SingletonVault {
  public:
-  SingletonVault() {};
+  enum class Type { Strict, Relaxed };
+
+  explicit SingletonVault(Type type = Type::Relaxed) : type_(type) {}
   ~SingletonVault();
 
   typedef std::function<void(void*)> TeardownFunc;
@@ -176,7 +178,7 @@ class SingletonVault {
                          TeardownFunc teardown) {
     std::lock_guard<std::mutex> guard(mutex_);
 
-    CHECK_THROW(state_ == SingletonVaultState::Registering, std::logic_error);
+    stateCheck(SingletonVaultState::Registering);
     CHECK_THROW(singletons_.find(type) == singletons_.end(), std::logic_error);
     auto& entry = singletons_[type];
     if (!entry) {
@@ -196,7 +198,7 @@ class SingletonVault {
   // registered at this point.
   void registrationComplete() {
     std::lock_guard<std::mutex> guard(mutex_);
-    CHECK_THROW(state_ == SingletonVaultState::Registering, std::logic_error);
+    stateCheck(SingletonVaultState::Registering);
     state_ = SingletonVaultState::Running;
   }
 
@@ -259,6 +261,13 @@ class SingletonVault {
     Living,
   };
 
+  void stateCheck(SingletonVaultState expected,
+                  const char* msg="Unexpected singleton state change") {
+    if (type_ == Type::Strict && expected != state_) {
+        throw std::logic_error(msg);
+    }
+  }
+
   // An actual instance of a singleton, tracking the instance itself,
   // its state as described above, and the create and teardown
   // functions.
@@ -292,12 +301,11 @@ class SingletonVault {
   SingletonEntry* get_entry(detail::TypeDescriptor type,
                             std::unique_lock<std::mutex>* lock) {
     // mutex must be held when calling this function
-    if (state_ != SingletonVaultState::Running) {
-      throw std::logic_error(
-          "Attempt to load a singleton before "
-          "SingletonVault::registrationComplete was called (hint: you probably "
-          "didn't call initFacebook)");
-    }
+    stateCheck(
+        SingletonVaultState::Running,
+        "Attempt to load a singleton before "
+        "SingletonVault::registrationComplete was called (hint: you probably "
+        "didn't call initFacebook)");
 
     auto it = singletons_.find(type);
     if (it == singletons_.end()) {
@@ -357,6 +365,7 @@ class SingletonVault {
                      detail::TypeDescriptorHasher> singletons_;
   std::vector<detail::TypeDescriptor> creation_order_;
   SingletonVaultState state_ = SingletonVaultState::Registering;
+  Type type_ = Type::Relaxed;
 };
 
 // This is the wrapper class that most users actually interact with.
index 3c3f19164cd77c9a8abbd058ac1b6c37a022c816..9c28e5ece0a0e667259d9d64ceefa2a680eab104 100644 (file)
@@ -174,7 +174,7 @@ TEST(Singleton, NamedUsage) {
 // Some pathological cases such as getting unregistered singletons,
 // double registration, etc.
 TEST(Singleton, NaughtyUsage) {
-  SingletonVault vault;
+  SingletonVault vault(SingletonVault::Type::Strict);
   vault.registrationComplete();
 
   // Unregistered.
@@ -188,10 +188,10 @@ TEST(Singleton, NaughtyUsage) {
                }(),
                std::logic_error);
 
-  EXPECT_THROW([]() { Singleton<Watchdog> watchdog_singleton; }(),
-               std::logic_error);
+  // Default vault is non-strict; this should work.
+  Singleton<Watchdog> global_watchdog_singleton;
 
-  SingletonVault vault_2;
+  SingletonVault vault_2(SingletonVault::Type::Strict);
   EXPECT_THROW(Singleton<Watchdog>::get(&vault_2), std::logic_error);
   Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault_2);
   // double registration