X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FSingleton.cpp;h=0800ac65d4bed8b20f31968d466db9aac0a6b32e;hp=67e6fd811cab7bac48635c1d628a53826df45ac4;hb=3f4a8ae005738662fbbca4a85c959df2bf0d059b;hpb=ed8c80a0e0988e4ce687f51ca832a00e4a6b7930 diff --git a/folly/Singleton.cpp b/folly/Singleton.cpp index 67e6fd81..0800ac65 100644 --- a/folly/Singleton.cpp +++ b/folly/Singleton.cpp @@ -16,17 +16,57 @@ #include +#ifndef _WIN32 +#include +#endif + #include #include #include #include #include +#include +#include #include +#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) { @@ -38,8 +78,113 @@ namespace detail { << 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 { @@ -66,13 +211,13 @@ FatalHelper fatalHelper; FatalHelper __attribute__ ((__init_priority__ (101))) fatalHelper; #endif -} +} // namespace SingletonVault::~SingletonVault() { destroyInstances(); } 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()."; @@ -85,7 +230,7 @@ void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) { 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()."; @@ -101,7 +246,7 @@ void SingletonVault::registrationComplete() { std::atexit([](){ SingletonVault::singleton()->destroyInstances(); }); auto state = state_.wlock(); - stateCheck(SingletonVaultState::Running, *state); + state->check(detail::SingletonVaultState::Type::Running); if (state->registrationComplete) { return; @@ -124,7 +269,7 @@ void SingletonVault::registrationComplete() { 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"); } @@ -139,7 +284,7 @@ void SingletonVault::doEagerInit() { 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"); } @@ -175,10 +320,10 @@ void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) { 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(); { @@ -220,28 +365,16 @@ void SingletonVault::destroyInstances() { 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() { // Add a dependency on folly::ThreadLocal to make sure all its static // singletons are initalized first. threadlocal_detail::StaticMeta::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; + std::atexit([] { SingletonVault::singleton()->destroyInstances(); }); } -} +} // namespace folly