template <typename T>
void SingletonHolder<T>::registerSingletonMock(CreateFunc c, TeardownFunc t) {
if (state_ == SingletonHolderState::NotRegistered) {
- LOG(FATAL) << "Registering mock before singleton was registered: "
- << type().name();
+ detail::singletonWarnRegisterMockEarlyAndAbort(type());
}
if (state_ == SingletonHolderState::Living) {
destroyInstance();
createInstance();
if (instance_weak_.expired()) {
- throw std::runtime_error(
- "Raw pointer to a singleton requested after its destruction."
- " Singleton type is: " +
- type().name());
+ detail::singletonThrowGetInvokedAfterDestruction(type());
}
return instance_ptr_;
teardown_(instance_ptr_);
} else {
print_destructor_stack_trace_->store(true);
- LOG(ERROR) << "Singleton of type " << type().name() << " has a "
- << "living reference at destroyInstances time; beware! Raw "
- << "pointer is " << instance_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.";
+ detail::singletonWarnDestroyInstanceLeak(type(), instance_ptr_);
}
}
}
void SingletonHolder<T>::createInstance() {
if (creating_thread_.load(std::memory_order_acquire) ==
std::this_thread::get_id()) {
- LOG(FATAL) << "circular singleton dependency: " << type().name();
+ detail::singletonWarnCreateCircularDependencyAndAbort(type());
}
std::lock_guard<std::mutex> entry_lock(mutex_);
}
if (state_.load(std::memory_order_acquire) ==
SingletonHolderState::NotRegistered) {
- auto ptr = SingletonVault::stackTraceGetter().load();
- LOG(FATAL) << "Creating instance for unregistered singleton: "
- << type().name() << "\n"
- << "Stacktrace:"
- << "\n"
- << (ptr ? (*ptr)() : "(not available)");
+ detail::singletonWarnCreateUnregisteredAndAbort(type());
}
if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) {
auto state = vault_.state_.rlock();
if (vault_.type_ != SingletonVault::Type::Relaxed &&
!state->registrationComplete) {
- 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;
+ detail::singletonWarnCreateBeforeRegistrationCompleteAndAbort(type());
}
- if (state->state == SingletonVault::SingletonVaultState::Quiescing) {
+ if (state->state == detail::SingletonVaultState::Type::Quiescing) {
return;
}
T*) mutable {
destroy_baton->post();
if (print_destructor_stack_trace->load()) {
- 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;
+ detail::singletonPrintDestructionStackTrace(type);
}
});
#include <iostream>
#include <string>
+#include <folly/Demangle.h>
+#include <folly/Format.h>
#include <folly/ScopeGuard.h>
#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__ANDROID__)
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) {
// Ensure the availability of std::cerr
<< 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 {
void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) {
auto state = state_.rlock();
- stateCheck(SingletonVaultState::Running, *state);
+ state->check(detail::SingletonVaultState::Type::Running);
if (UNLIKELY(state->registrationComplete)) {
LOG(ERROR) << "Registering singleton after registrationComplete().";
void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
auto state = state_.rlock();
- stateCheck(SingletonVaultState::Running, *state);
+ state->check(detail::SingletonVaultState::Type::Running);
if (UNLIKELY(state->registrationComplete)) {
LOG(ERROR) << "Registering for eager-load after registrationComplete().";
std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
auto state = state_.wlock();
- stateCheck(SingletonVaultState::Running, *state);
+ state->check(detail::SingletonVaultState::Type::Running);
if (state->registrationComplete) {
return;
void SingletonVault::doEagerInit() {
{
auto state = state_.rlock();
- stateCheck(SingletonVaultState::Running, *state);
+ state->check(detail::SingletonVaultState::Type::Running);
if (UNLIKELY(!state->registrationComplete)) {
throw std::logic_error("registrationComplete() not yet called");
}
void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) {
{
auto state = state_.rlock();
- stateCheck(SingletonVaultState::Running, *state);
+ state->check(detail::SingletonVaultState::Type::Running);
if (UNLIKELY(!state->registrationComplete)) {
throw std::logic_error("registrationComplete() not yet called");
}
void SingletonVault::destroyInstances() {
auto stateW = state_.wlock();
- if (stateW->state == SingletonVaultState::Quiescing) {
+ if (stateW->state == detail::SingletonVaultState::Type::Quiescing) {
return;
}
- stateW->state = SingletonVaultState::Quiescing;
+ stateW->state = detail::SingletonVaultState::Type::Quiescing;
auto stateR = stateW.moveFromWriteToRead();
{
void SingletonVault::reenableInstances() {
auto state = state_.wlock();
- stateCheck(SingletonVaultState::Quiescing, *state);
+ state->check(detail::SingletonVaultState::Type::Quiescing);
- state->state = SingletonVaultState::Running;
+ state->state = detail::SingletonVaultState::Type::Running;
}
void SingletonVault::scheduleDestroyInstances() {
// should call reenableInstances.
#pragma once
-#include <folly/Demangle.h>
+
#include <folly/Exception.h>
#include <folly/Executor.h>
#include <folly/Memory.h>
return *this;
}
- std::string name() const {
- auto ret = demangle(ti_.name());
- if (tag_ti_ != std::type_index(typeid(DefaultTag))) {
- ret += "/";
- ret += demangle(tag_ti_.name());
- }
- return ret.toStdString();
- }
+ std::string name() const;
friend class TypeDescriptorHasher;
}
};
+[[noreturn]] void singletonWarnLeakyDoubleRegistrationAndAbort(
+ const TypeDescriptor& type);
+
+[[noreturn]] void singletonWarnLeakyInstantiatingNotRegisteredAndAbort(
+ const TypeDescriptor& type);
+
+[[noreturn]] void singletonWarnRegisterMockEarlyAndAbort(
+ const TypeDescriptor& type);
+
+void singletonWarnDestroyInstanceLeak(
+ const TypeDescriptor& type,
+ const void* ptr);
+
+[[noreturn]] void singletonWarnCreateCircularDependencyAndAbort(
+ const TypeDescriptor& type);
+
+[[noreturn]] void singletonWarnCreateUnregisteredAndAbort(
+ const TypeDescriptor& type);
+
+[[noreturn]] void singletonWarnCreateBeforeRegistrationCompleteAndAbort(
+ const TypeDescriptor& type);
+
+void singletonPrintDestructionStackTrace(const TypeDescriptor& type);
+
+[[noreturn]] void singletonThrowNullCreator(const std::type_info& type);
+
+[[noreturn]] void singletonThrowGetInvokedAfterDestruction(
+ const TypeDescriptor& type);
+
+struct SingletonVaultState {
+ // The two stages of life for a vault, as mentioned in the class comment.
+ enum class Type {
+ Running,
+ Quiescing,
+ };
+
+ Type state{Type::Running};
+ bool registrationComplete{false};
+
+ // Each singleton in the vault can be in two states: dead
+ // (registered but never created), living (CreateFunc returned an instance).
+
+ void check(
+ Type expected,
+ const char* msg = "Unexpected singleton state change") const {
+ if (expected != state) {
+ throwUnexpectedState(msg);
+ }
+ }
+
+ [[noreturn]] static void throwUnexpectedState(const char* msg);
+};
+
// This interface is used by SingletonVault to interact with SingletonHolders.
// Having a non-template interface allows SingletonVault to keep a list of all
// SingletonHolders.
template <typename T>
friend struct detail::SingletonHolder;
- // The two stages of life for a vault, as mentioned in the class comment.
- enum class SingletonVaultState {
- Running,
- Quiescing,
- };
-
- struct State {
- SingletonVaultState state{SingletonVaultState::Running};
- bool registrationComplete{false};
- };
-
- // Each singleton in the vault can be in two states: dead
- // (registered but never created), living (CreateFunc returned an instance).
-
- static void stateCheck(
- SingletonVaultState expected,
- const State& state,
- const char* msg = "Unexpected singleton state change") {
- if (expected != state.state) {
- throw std::logic_error(msg);
- }
- }
-
// This method only matters if registrationComplete() is never called.
// Otherwise destroyInstances is scheduled to be executed atexit.
//
typedef std::unordered_map<detail::TypeDescriptor,
detail::SingletonHolderBase*,
detail::TypeDescriptorHasher> SingletonMap;
- folly::Synchronized<SingletonMap> singletons_;
- folly::Synchronized<std::unordered_set<detail::SingletonHolderBase*>>
+ Synchronized<SingletonMap> singletons_;
+ Synchronized<std::unordered_set<detail::SingletonHolderBase*>>
eagerInitSingletons_;
- folly::Synchronized<std::vector<detail::TypeDescriptor>> creationOrder_;
+ Synchronized<std::vector<detail::TypeDescriptor>> creationOrder_;
// Using SharedMutexReadPriority is important here, because we want to make
// sure we don't block nested singleton creation happening concurrently with
// destroyInstances().
- folly::Synchronized<State, folly::SharedMutexReadPriority> state_;
+ Synchronized<detail::SingletonVaultState, SharedMutexReadPriority> state_;
Type type_;
};
explicit Singleton(typename Singleton::CreateFunc c,
typename Singleton::TeardownFunc t = nullptr) {
if (c == nullptr) {
- throw std::logic_error(
- "nullptr_t should be passed if you want T to be default constructed");
+ detail::singletonThrowNullCreator(typeid(T));
}
auto vault = SingletonVault::singleton<VaultTag>();
static void make_mock(CreateFunc c,
typename Singleton<T>::TeardownFunc t = nullptr) {
if (c == nullptr) {
- throw std::logic_error(
- "nullptr_t should be passed if you want T to be default constructed");
+ detail::singletonThrowNullCreator(typeid(T));
}
auto& entry = getEntry();
explicit LeakySingleton(CreateFunc createFunc) {
auto& entry = entryInstance();
if (entry.state != State::NotRegistered) {
- LOG(FATAL) << "Double registration of singletons of the same "
- << "underlying type; check for multiple definitions "
- << "of type folly::LeakySingleton<" + entry.type_.name() + ">";
+ detail::singletonWarnLeakyDoubleRegistrationAndAbort(entry.type_);
}
entry.createFunc = createFunc;
entry.state = State::Dead;
}
static void make_mock(CreateFunc createFunc) {
- auto& entry = entryInstance();
if (createFunc == nullptr) {
- throw std::logic_error(
- "nullptr_t should be passed if you want T to be default constructed");
+ detail::singletonThrowNullCreator(typeid(T));
}
+ auto& entry = entryInstance();
entry.createFunc = createFunc;
entry.state = State::Dead;
}
}
if (entry.state == State::NotRegistered) {
- auto ptr = SingletonVault::stackTraceGetter().load();
- LOG(FATAL) << "Creating instance for unregistered singleton: "
- << entry.type_.name() << "\n"
- << "Stacktrace:"
- << "\n" << (ptr ? (*ptr)() : "(not available)");
+ detail::singletonWarnLeakyInstantiatingNotRegisteredAndAbort(entry.type_);
}
entry.ptr = entry.createFunc();