(wangle) fix after-delete assert
[folly.git] / folly / experimental / Singleton.h
index cbbd03199503b4b281461b86130550836f27ae36..1d6c9a78c83397bee0216bf61870986cbab2ea29 100644 (file)
@@ -74,6 +74,9 @@
 // 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.
+// Singletons won't be recreated after destroyInstances call. If you
+// want to re-enable singleton creation (say after fork was called) you
+// should call reenableInstances.
 
 #pragma once
 #include <folly/Exception.h>
@@ -179,9 +182,16 @@ class SingletonVault {
   void registerSingleton(detail::TypeDescriptor type,
                          CreateFunc create,
                          TeardownFunc teardown) {
+    RWSpinLock::ReadHolder rh(&stateMutex_);
+
+    stateCheck(SingletonVaultState::Running);
+    if (UNLIKELY(registrationComplete_)) {
+      throw std::logic_error(
+        "Registering singleton after registrationComplete().");
+    }
+
     RWSpinLock::WriteHolder wh(&mutex_);
 
-    stateCheck(SingletonVaultState::Registering);
     CHECK_THROW(singletons_.find(type) == singletons_.end(), std::logic_error);
     auto& entry = singletons_[type];
     entry.reset(new SingletonEntry);
@@ -198,20 +208,34 @@ class SingletonVault {
   // Mark registration is complete; no more singletons can be
   // registered at this point.
   void registrationComplete() {
-    RWSpinLock::WriteHolder wh(&mutex_);
+    RWSpinLock::WriteHolder wh(&stateMutex_);
+
+    stateCheck(SingletonVaultState::Running);
 
-    stateCheck(SingletonVaultState::Registering);
-    state_ = SingletonVaultState::Running;
+    if (type_ == Type::Strict) {
+      for (const auto& id_singleton_entry: singletons_) {
+        const auto& singleton_entry = *id_singleton_entry.second;
+        if (singleton_entry.state != SingletonEntryState::Dead) {
+          throw std::runtime_error(
+            "Singleton created before registration was complete.");
+        }
+      }
+    }
+
+    registrationComplete_ = true;
   }
 
-  // Destroy all singletons; when complete, the vault can create
-  // singletons once again, or remain dormant.
+  // Destroy all singletons; when complete, the vault can't create
+  // singletons once again until reenableInstances() is called.
   void destroyInstances();
 
+  // Enable re-creating singletons after destroyInstances() was called.
+  void reenableInstances();
+
   // Retrieve a singleton from the vault, creating it if necessary.
-  std::shared_ptr<void> get_shared(detail::TypeDescriptor type) {
+  std::weak_ptr<void> get_weak(detail::TypeDescriptor type) {
     auto entry = get_entry_create(type);
-    return entry->instance;
+    return entry->instance_weak;
   }
 
   // This function is inherently racy since we don't hold the
@@ -220,6 +244,10 @@ class SingletonVault {
   // the weak_ptr interface for true safety.
   void* get_ptr(detail::TypeDescriptor type) {
     auto entry = get_entry_create(type);
+    if (UNLIKELY(entry->instance_weak.expired())) {
+      throw std::runtime_error(
+        "Raw pointer to a singleton requested after its destruction.");
+    }
     return entry->instance_ptr;
   }
 
@@ -251,8 +279,8 @@ class SingletonVault {
  private:
   // The two stages of life for a vault, as mentioned in the class comment.
   enum class SingletonVaultState {
-    Registering,
     Running,
+    Quiescing,
   };
 
   // Each singleton in the vault can be in three states: dead
@@ -266,7 +294,7 @@ class SingletonVault {
 
   void stateCheck(SingletonVaultState expected,
                   const char* msg="Unexpected singleton state change") {
-    if (type_ == Type::Strict && expected != state_) {
+    if (expected != state_) {
         throw std::logic_error(msg);
     }
   }
@@ -287,6 +315,7 @@ class SingletonVault {
 
     // The singleton itself and related functions.
     std::shared_ptr<void> instance;
+    std::weak_ptr<void> instance_weak;
     void* instance_ptr = nullptr;
     CreateFunc create = nullptr;
     TeardownFunc teardown = nullptr;
@@ -298,16 +327,19 @@ class SingletonVault {
     SingletonEntry(SingletonEntry&&) = delete;
   };
 
+  // Initializes static object, which calls destroyInstances on destruction.
+  // Used to have better deletion ordering with singleton not managed by
+  // folly::Singleton. The desruction will happen in the following order:
+  // 1. Singletons, not managed by folly::Singleton, which were created after
+  //    any of the singletons managed by folly::Singleton was requested.
+  // 2. All singletons managed by folly::Singleton
+  // 3. Singletons, not managed by folly::Singleton, which were created before
+  //    any of the singletons managed by folly::Singleton was requested.
+  static void scheduleDestroyInstances();
+
   SingletonEntry* get_entry(detail::TypeDescriptor type) {
     RWSpinLock::ReadHolder rh(&mutex_);
 
-    // mutex must be held when calling this function
-    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()) {
       throw std::out_of_range(std::string("non-existent singleton: ") +
@@ -339,6 +371,11 @@ class SingletonVault {
     }
 
     if (entry->instance == nullptr) {
+      RWSpinLock::ReadHolder rh(&stateMutex_);
+      if (state_ == SingletonVaultState::Quiescing) {
+        return entry;
+      }
+
       CHECK(entry->state == SingletonEntryState::Dead);
       entry->state = SingletonEntryState::BeingBorn;
       entry->creating_thread = std::this_thread::get_id();
@@ -346,10 +383,18 @@ class SingletonVault {
       entry_lock.unlock();
       // Can't use make_shared -- no support for a custom deleter, sadly.
       auto instance = std::shared_ptr<void>(entry->create(), entry->teardown);
+
+      // We should schedule destroyInstances() only after the singleton was
+      // created. This will ensure it will be destroyed before singletons,
+      // not managed by folly::Singleton, which were initialized in its
+      // constructor
+      scheduleDestroyInstances();
+
       entry_lock.lock();
 
       CHECK(entry->state == SingletonEntryState::BeingBorn);
       entry->instance = instance;
+      entry->instance_weak = instance;
       entry->instance_ptr = instance.get();
       entry->state = SingletonEntryState::Living;
       entry->state_condvar.notify_all();
@@ -370,8 +415,10 @@ class SingletonVault {
                      SingletonEntryPtr,
                      detail::TypeDescriptorHasher> singletons_;
   std::vector<detail::TypeDescriptor> creation_order_;
-  SingletonVaultState state_ = SingletonVaultState::Registering;
-  Type type_ = Type::Relaxed;
+  SingletonVaultState state_{SingletonVaultState::Running};
+  bool registrationComplete_{false};
+  folly::RWSpinLock stateMutex_;
+  Type type_{Type::Relaxed};
 };
 
 // This is the wrapper class that most users actually interact with.
@@ -408,7 +455,16 @@ class Singleton {
 
   static std::weak_ptr<T> get_weak(
       const char* name, SingletonVault* vault = nullptr /* for testing */) {
-    return std::weak_ptr<T>(get_shared({typeid(T), name}, vault));
+    auto weak_void_ptr =
+      (vault ?: SingletonVault::singleton())->get_weak({typeid(T), name});
+
+    // This is ugly and inefficient, but there's no other way to do it, because
+    // there's no static_pointer_cast for weak_ptr.
+    auto shared_void_ptr = weak_void_ptr.lock();
+    if (!shared_void_ptr) {
+      return std::weak_ptr<T>();
+    }
+    return std::static_pointer_cast<T>(shared_void_ptr);
   }
 
   // Allow the Singleton<t> instance to also retrieve the underlying
@@ -417,25 +473,38 @@ class Singleton {
   T& operator*() { return *ptr(); }
   T* operator->() { return ptr(); }
 
-  explicit Singleton(Singleton::CreateFunc c = nullptr,
+  template <typename CreateFunc = std::nullptr_t>
+  explicit Singleton(CreateFunc c = nullptr,
                      Singleton::TeardownFunc t = nullptr,
                      SingletonVault* vault = nullptr /* for testing */)
       : Singleton({typeid(T), ""}, c, t, vault) {}
 
+  template <typename CreateFunc = std::nullptr_t>
   explicit Singleton(const char* name,
-                     Singleton::CreateFunc c = nullptr,
+                     CreateFunc c = nullptr,
                      Singleton::TeardownFunc t = nullptr,
                      SingletonVault* vault = nullptr /* for testing */)
       : Singleton({typeid(T), name}, c, t, vault) {}
 
  private:
   explicit Singleton(detail::TypeDescriptor type,
-                     Singleton::CreateFunc c = nullptr,
-                     Singleton::TeardownFunc t = nullptr,
-                     SingletonVault* vault = nullptr /* for testing */)
+                     std::nullptr_t,
+                     Singleton::TeardownFunc t,
+                     SingletonVault* vault) :
+      Singleton (type,
+                 []() { return new T; },
+                 std::move(t),
+                 vault) {
+  }
+
+  explicit Singleton(detail::TypeDescriptor type,
+                     Singleton::CreateFunc c,
+                     Singleton::TeardownFunc t,
+                     SingletonVault* vault)
       : type_descriptor_(type) {
     if (c == nullptr) {
-      c = []() { return new T; };
+      throw std::logic_error(
+        "nullptr_t should be passed if you want T to be default constructed");
     }
     SingletonVault::TeardownFunc teardown;
     if (t == nullptr) {
@@ -471,7 +540,7 @@ class Singleton {
       detail::TypeDescriptor type_descriptor = {typeid(T), ""},
       SingletonVault* vault = nullptr /* for testing */) {
     return std::static_pointer_cast<T>(
-        (vault ?: SingletonVault::singleton())->get_shared(type_descriptor));
+      (vault ?: SingletonVault::singleton())->get_weak(type_descriptor).lock());
   }
 
   detail::TypeDescriptor type_descriptor_;