/*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2014-present 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 <folly/Singleton.h>
+#ifndef _WIN32
+#include <dlfcn.h>
+#endif
+
#include <atomic>
#include <cstdio>
#include <cstdlib>
-#include <sstream>
+#include <iostream>
#include <string>
-#include <folly/FileUtil.h>
+#include <folly/Demangle.h>
+#include <folly/Format.h>
#include <folly/ScopeGuard.h>
+#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__ANDROID__)
+#define FOLLY_SINGLETON_HAVE_DLSYM 1
+#endif
+
namespace folly {
+#if FOLLY_SINGLETON_HAVE_DLSYM
namespace detail {
+static void singleton_hs_init_weak(int* argc, char** argv[])
+ __attribute__((__weakref__("hs_init")));
+} // namespace detail
+#endif
+
+SingletonVault::Type SingletonVault::defaultVaultType() {
+#if FOLLY_SINGLETON_HAVE_DLSYM
+ bool isPython = dlsym(RTLD_DEFAULT, "Py_Main");
+ bool isHaskel =
+ detail::singleton_hs_init_weak || dlsym(RTLD_DEFAULT, "hs_init");
+ bool isJVM = dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs");
+ bool isD = dlsym(RTLD_DEFAULT, "_d_run_main");
+
+ return isPython || isHaskel || isJVM || isD ? Type::Relaxed : Type::Strict;
+#else
+ return Type::Relaxed;
+#endif
+}
+
+namespace detail {
+
+std::string TypeDescriptor::name() const {
+ auto ret = demangle(ti_.name());
+ if (tag_ti_ != std::type_index(typeid(DefaultTag))) {
+ ret += "/";
+ ret += demangle(tag_ti_.name());
+ }
+ return ret.toStdString();
+}
[[noreturn]] void singletonWarnDoubleRegistrationAndAbort(
const TypeDescriptor& type) {
- // Not using LOG(FATAL) or std::cerr because they may not be initialized yet.
- std::ostringstream o;
- o << "Double registration of singletons of the same "
- << "underlying type; check for multiple definitions "
- << "of type folly::Singleton<" << type.name() << ">" << std::endl;
- auto s = o.str();
- writeFull(STDERR_FILENO, s.data(), s.size());
+ // Ensure the availability of std::cerr
+ std::ios_base::Init ioInit;
+ std::cerr << "Double registration of singletons of the same "
+ "underlying type; check for multiple definitions "
+ "of type folly::Singleton<"
+ << type.name() << ">\n";
std::abort();
}
+
+[[noreturn]] void singletonWarnLeakyDoubleRegistrationAndAbort(
+ const TypeDescriptor& type) {
+ // Ensure the availability of std::cerr
+ std::ios_base::Init ioInit;
+ std::cerr << "Double registration of singletons of the same "
+ "underlying type; check for multiple definitions "
+ "of type folly::LeakySingleton<"
+ << type.name() << ">\n";
+ std::abort();
+}
+
+[[noreturn]] void singletonWarnLeakyInstantiatingNotRegisteredAndAbort(
+ const TypeDescriptor& type) {
+ auto ptr = SingletonVault::stackTraceGetter().load();
+ LOG(FATAL) << "Creating instance for unregistered singleton: "
+ << type.name() << "\n"
+ << "Stacktrace:"
+ << "\n" << (ptr ? (*ptr)() : "(not available)");
+}
+
+[[noreturn]] void singletonWarnRegisterMockEarlyAndAbort(
+ const TypeDescriptor& type) {
+ LOG(FATAL) << "Registering mock before singleton was registered: "
+ << type.name();
+}
+
+void singletonWarnDestroyInstanceLeak(
+ const TypeDescriptor& type,
+ const void* ptr) {
+ LOG(ERROR) << "Singleton of type " << type.name() << " has a "
+ << "living reference at destroyInstances time; beware! Raw "
+ << "pointer is " << ptr << ". It is very likely "
+ << "that some other singleton is holding a shared_ptr to it. "
+ << "This singleton will be leaked (even if a shared_ptr to it "
+ << "is eventually released)."
+ << "Make sure dependencies between these singletons are "
+ << "properly defined.";
+}
+
+[[noreturn]] void singletonWarnCreateCircularDependencyAndAbort(
+ const TypeDescriptor& type) {
+ LOG(FATAL) << "circular singleton dependency: " << type.name();
}
+[[noreturn]] void singletonWarnCreateUnregisteredAndAbort(
+ const TypeDescriptor& type) {
+ auto ptr = SingletonVault::stackTraceGetter().load();
+ LOG(FATAL) << "Creating instance for unregistered singleton: "
+ << type.name() << "\n"
+ << "Stacktrace:"
+ << "\n"
+ << (ptr ? (*ptr)() : "(not available)");
+}
+
+[[noreturn]] void singletonWarnCreateBeforeRegistrationCompleteAndAbort(
+ const TypeDescriptor& type) {
+ auto stack_trace_getter = SingletonVault::stackTraceGetter().load();
+ auto stack_trace = stack_trace_getter ? stack_trace_getter() : "";
+ if (!stack_trace.empty()) {
+ stack_trace = "Stack trace:\n" + stack_trace;
+ }
+
+ LOG(FATAL) << "Singleton " << type.name() << " requested before "
+ << "registrationComplete() call.\n"
+ << "This usually means that either main() never called "
+ << "folly::init, or singleton was requested before main() "
+ << "(which is not allowed).\n"
+ << stack_trace;
+}
+
+void singletonPrintDestructionStackTrace(const TypeDescriptor& type) {
+ std::string output = "Singleton " + type.name() + " was released.\n";
+
+ auto stack_trace_getter = SingletonVault::stackTraceGetter().load();
+ auto stack_trace = stack_trace_getter ? stack_trace_getter() : "";
+ if (stack_trace.empty()) {
+ output += "Failed to get release stack trace.";
+ } else {
+ output += "Release stack trace:\n";
+ output += stack_trace;
+ }
+
+ LOG(ERROR) << output;
+}
+
+[[noreturn]] void singletonThrowNullCreator(const std::type_info& type) {
+ auto const msg = sformat(
+ "nullptr_t should be passed if you want {} to be default constructed",
+ demangle(type));
+ throw std::logic_error(msg);
+}
+
+[[noreturn]] void singletonThrowGetInvokedAfterDestruction(
+ const TypeDescriptor& type) {
+ throw std::runtime_error(
+ "Raw pointer to a singleton requested after its destruction."
+ " Singleton type is: " +
+ type.name());
+}
+
+[[noreturn]] void SingletonVaultState::throwUnexpectedState(const char* msg) {
+ throw std::logic_error(msg);
+}
+
+} // namespace detail
+
namespace {
struct FatalHelper {
FatalHelper __attribute__ ((__init_priority__ (101))) fatalHelper;
#endif
-}
+} // namespace
SingletonVault::~SingletonVault() { destroyInstances(); }
void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) {
- RWSpinLock::ReadHolder rh(&stateMutex_);
+ auto state = state_.rlock();
+ state->check(detail::SingletonVaultState::Type::Running);
- stateCheck(SingletonVaultState::Running);
-
- if (UNLIKELY(registrationComplete_)) {
+ if (UNLIKELY(state->registrationComplete)) {
LOG(ERROR) << "Registering singleton after registrationComplete().";
}
- RWSpinLock::ReadHolder rhMutex(&mutex_);
- CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(),
- std::logic_error);
-
- RWSpinLock::UpgradedHolder wh(&mutex_);
- singletons_[entry->type()] = entry;
+ auto singletons = singletons_.wlock();
+ CHECK_THROW(
+ singletons->emplace(entry->type(), entry).second, std::logic_error);
}
void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
- RWSpinLock::ReadHolder rh(&stateMutex_);
+ auto state = state_.rlock();
+ state->check(detail::SingletonVaultState::Type::Running);
- stateCheck(SingletonVaultState::Running);
-
- if (UNLIKELY(registrationComplete_)) {
+ if (UNLIKELY(state->registrationComplete)) {
LOG(ERROR) << "Registering for eager-load after registrationComplete().";
}
- RWSpinLock::ReadHolder rhMutex(&mutex_);
- CHECK_THROW(singletons_.find(entry->type()) != singletons_.end(),
- std::logic_error);
+ CHECK_THROW(singletons_.rlock()->count(entry->type()), std::logic_error);
- RWSpinLock::UpgradedHolder wh(&mutex_);
- eagerInitSingletons_.insert(entry);
+ auto eagerInitSingletons = eagerInitSingletons_.wlock();
+ eagerInitSingletons->insert(entry);
}
void SingletonVault::registrationComplete() {
std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
- RWSpinLock::WriteHolder wh(&stateMutex_);
+ auto state = state_.wlock();
+ state->check(detail::SingletonVaultState::Type::Running);
- stateCheck(SingletonVaultState::Running);
+ if (state->registrationComplete) {
+ return;
+ }
+ auto singletons = singletons_.rlock();
if (type_ == Type::Strict) {
- for (const auto& p : singletons_) {
+ for (const auto& p : *singletons) {
if (p.second->hasLiveInstance()) {
throw std::runtime_error(
- "Singleton created before registration was complete.");
+ "Singleton " + p.first.name() +
+ " created before registration was complete.");
}
}
}
- registrationComplete_ = true;
+ state->registrationComplete = true;
}
void SingletonVault::doEagerInit() {
- std::unordered_set<detail::SingletonHolderBase*> singletonSet;
{
- RWSpinLock::ReadHolder rh(&stateMutex_);
- stateCheck(SingletonVaultState::Running);
- if (UNLIKELY(!registrationComplete_)) {
+ auto state = state_.rlock();
+ state->check(detail::SingletonVaultState::Type::Running);
+ if (UNLIKELY(!state->registrationComplete)) {
throw std::logic_error("registrationComplete() not yet called");
}
- singletonSet = eagerInitSingletons_; // copy set of pointers
}
- for (auto *single : singletonSet) {
+ auto eagerInitSingletons = eagerInitSingletons_.rlock();
+ for (auto* single : *eagerInitSingletons) {
single->createInstance();
}
}
void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) {
- std::unordered_set<detail::SingletonHolderBase*> singletonSet;
{
- RWSpinLock::ReadHolder rh(&stateMutex_);
- stateCheck(SingletonVaultState::Running);
- if (UNLIKELY(!registrationComplete_)) {
+ auto state = state_.rlock();
+ state->check(detail::SingletonVaultState::Type::Running);
+ if (UNLIKELY(!state->registrationComplete)) {
throw std::logic_error("registrationComplete() not yet called");
}
- singletonSet = eagerInitSingletons_; // copy set of pointers
}
- auto countdown = std::make_shared<std::atomic<size_t>>(singletonSet.size());
- for (auto* single : singletonSet) {
+ auto eagerInitSingletons = eagerInitSingletons_.rlock();
+ auto countdown =
+ std::make_shared<std::atomic<size_t>>(eagerInitSingletons->size());
+ for (auto* single : *eagerInitSingletons) {
// countdown is retained by shared_ptr, and will be alive until last lambda
// is done. notifyBaton is provided by the caller, and expected to remain
// present (if it's non-nullptr). singletonSet can go out of scope but
}
void SingletonVault::destroyInstances() {
- RWSpinLock::WriteHolder state_wh(&stateMutex_);
-
- if (state_ == SingletonVaultState::Quiescing) {
+ auto stateW = state_.wlock();
+ if (stateW->state == detail::SingletonVaultState::Type::Quiescing) {
return;
}
- state_ = SingletonVaultState::Quiescing;
-
- RWSpinLock::ReadHolder state_rh(std::move(state_wh));
+ stateW->state = detail::SingletonVaultState::Type::Quiescing;
+ auto stateR = stateW.moveFromWriteToRead();
{
- RWSpinLock::ReadHolder rh(&mutex_);
+ auto singletons = singletons_.rlock();
+ auto creationOrder = creationOrder_.rlock();
+
+ CHECK_GE(singletons->size(), creationOrder->size());
- CHECK_GE(singletons_.size(), creation_order_.size());
+ // Release all ReadMostlyMainPtrs at once
+ {
+ ReadMostlyMainPtrDeleter<> deleter;
+ for (auto& singleton_type : *creationOrder) {
+ singletons->at(singleton_type)->preDestroyInstance(deleter);
+ }
+ }
- for (auto type_iter = creation_order_.rbegin();
- type_iter != creation_order_.rend();
+ for (auto type_iter = creationOrder->rbegin();
+ type_iter != creationOrder->rend();
++type_iter) {
- singletons_[*type_iter]->destroyInstance();
+ singletons->at(*type_iter)->destroyInstance();
}
- for (auto& singleton_type: creation_order_) {
- auto singleton = singletons_[singleton_type];
+ for (auto& singleton_type : *creationOrder) {
+ auto singleton = singletons->at(singleton_type);
if (!singleton->hasLiveInstance()) {
continue;
}
}
{
- RWSpinLock::WriteHolder wh(&mutex_);
- creation_order_.clear();
+ auto creationOrder = creationOrder_.wlock();
+ creationOrder->clear();
}
}
void SingletonVault::reenableInstances() {
- RWSpinLock::WriteHolder state_wh(&stateMutex_);
+ auto state = state_.wlock();
- stateCheck(SingletonVaultState::Quiescing);
+ state->check(detail::SingletonVaultState::Type::Quiescing);
- state_ = SingletonVaultState::Running;
+ state->state = detail::SingletonVaultState::Type::Running;
}
void SingletonVault::scheduleDestroyInstances() {
// Add a dependency on folly::ThreadLocal to make sure all its static
// singletons are initalized first.
- threadlocal_detail::StaticMeta<void>::instance();
-
- class SingletonVaultDestructor {
- public:
- ~SingletonVaultDestructor() {
- SingletonVault::singleton()->destroyInstances();
- }
- };
-
- // Here we intialize a singleton, which calls destroyInstances in its
- // destructor. Because of singleton destruction order - it will be destroyed
- // before all the singletons, which were initialized before it and after all
- // the singletons initialized after it.
- static SingletonVaultDestructor singletonVaultDestructor;
+ threadlocal_detail::StaticMeta<void, void>::instance();
+ std::atexit([] { SingletonVault::singleton()->destroyInstances(); });
}
-}
+} // namespace folly