// 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>
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);
// 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
// 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;
}
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
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);
}
}
// 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;
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: ") +
}
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();
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();
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.
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
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) {
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_;