367 lines
11 KiB
C++
367 lines
11 KiB
C++
|
/*
|
||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#include <folly/Singleton.h>
|
||
|
#include <folly/portability/Config.h>
|
||
|
|
||
|
#ifndef _WIN32
|
||
|
#include <dlfcn.h>
|
||
|
#endif
|
||
|
|
||
|
#include <atomic>
|
||
|
#include <cstdio>
|
||
|
#include <cstdlib>
|
||
|
#include <iostream>
|
||
|
#include <string>
|
||
|
|
||
|
#include <folly/Demangle.h>
|
||
|
#include <folly/Format.h>
|
||
|
#include <folly/ScopeGuard.h>
|
||
|
#include <folly/detail/SingletonStackTrace.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();
|
||
|
}
|
||
|
|
||
|
// clang-format off
|
||
|
[[noreturn]] void singletonWarnDoubleRegistrationAndAbort(
|
||
|
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::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 trace = detail::getSingletonStackTrace();
|
||
|
LOG(FATAL) << "Creating instance for unregistered singleton: " << type.name()
|
||
|
<< "\n"
|
||
|
<< "Stacktrace:\n" << (!trace.empty() ? trace : "(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 trace = detail::getSingletonStackTrace();
|
||
|
LOG(FATAL) << "Creating instance for unregistered singleton: " << type.name()
|
||
|
<< "\n"
|
||
|
<< "Stacktrace:\n" << (!trace.empty() ? trace : "(not available)");
|
||
|
}
|
||
|
|
||
|
[[noreturn]] void singletonWarnCreateBeforeRegistrationCompleteAndAbort(
|
||
|
const TypeDescriptor& type) {
|
||
|
auto trace = detail::getSingletonStackTrace();
|
||
|
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"
|
||
|
<< "Stacktrace:\n" << (!trace.empty() ? trace : "(not available)");
|
||
|
}
|
||
|
|
||
|
void singletonPrintDestructionStackTrace(const TypeDescriptor& type) {
|
||
|
auto trace = detail::getSingletonStackTrace();
|
||
|
LOG(ERROR) << "Singleton " << type.name() << " was released.\n"
|
||
|
<< "Stacktrace:\n" << (!trace.empty() ? trace : "(not available)");
|
||
|
}
|
||
|
|
||
|
[[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());
|
||
|
}
|
||
|
// clang-format on
|
||
|
|
||
|
} // namespace detail
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
struct FatalHelper {
|
||
|
~FatalHelper() {
|
||
|
if (!leakedSingletons_.empty()) {
|
||
|
std::string leakedTypes;
|
||
|
for (const auto& singleton : leakedSingletons_) {
|
||
|
leakedTypes += "\t" + singleton.name() + "\n";
|
||
|
}
|
||
|
LOG(DFATAL) << "Singletons of the following types had living references "
|
||
|
<< "after destroyInstances was finished:\n"
|
||
|
<< leakedTypes
|
||
|
<< "beware! It is very likely that those singleton instances "
|
||
|
<< "are leaked.";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::vector<detail::TypeDescriptor> leakedSingletons_;
|
||
|
};
|
||
|
|
||
|
#if defined(__APPLE__) || defined(_MSC_VER)
|
||
|
// OS X doesn't support constructor priorities.
|
||
|
FatalHelper fatalHelper;
|
||
|
#else
|
||
|
FatalHelper __attribute__((__init_priority__(101))) fatalHelper;
|
||
|
#endif
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
SingletonVault::~SingletonVault() {
|
||
|
destroyInstances();
|
||
|
}
|
||
|
|
||
|
void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) {
|
||
|
auto state = state_.rlock();
|
||
|
state->check(detail::SingletonVaultState::Type::Running);
|
||
|
|
||
|
if (UNLIKELY(state->registrationComplete)) {
|
||
|
LOG(ERROR) << "Registering singleton after registrationComplete().";
|
||
|
}
|
||
|
|
||
|
auto singletons = singletons_.wlock();
|
||
|
CHECK_THROW(
|
||
|
singletons->emplace(entry->type(), entry).second, std::logic_error);
|
||
|
}
|
||
|
|
||
|
void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
|
||
|
auto state = state_.rlock();
|
||
|
state->check(detail::SingletonVaultState::Type::Running);
|
||
|
|
||
|
if (UNLIKELY(state->registrationComplete)) {
|
||
|
LOG(ERROR) << "Registering for eager-load after registrationComplete().";
|
||
|
}
|
||
|
|
||
|
CHECK_THROW(singletons_.rlock()->count(entry->type()), std::logic_error);
|
||
|
|
||
|
auto eagerInitSingletons = eagerInitSingletons_.wlock();
|
||
|
eagerInitSingletons->insert(entry);
|
||
|
}
|
||
|
|
||
|
void SingletonVault::registrationComplete() {
|
||
|
std::atexit([]() { SingletonVault::singleton()->destroyInstances(); });
|
||
|
|
||
|
auto state = state_.wlock();
|
||
|
state->check(detail::SingletonVaultState::Type::Running);
|
||
|
|
||
|
if (state->registrationComplete) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
auto singletons = singletons_.rlock();
|
||
|
if (type_ == Type::Strict) {
|
||
|
for (const auto& p : *singletons) {
|
||
|
if (p.second->hasLiveInstance()) {
|
||
|
throw std::runtime_error(
|
||
|
"Singleton " + p.first.name() +
|
||
|
" created before registration was complete.");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
state->registrationComplete = true;
|
||
|
}
|
||
|
|
||
|
void SingletonVault::doEagerInit() {
|
||
|
{
|
||
|
auto state = state_.rlock();
|
||
|
state->check(detail::SingletonVaultState::Type::Running);
|
||
|
if (UNLIKELY(!state->registrationComplete)) {
|
||
|
throw std::logic_error("registrationComplete() not yet called");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
auto eagerInitSingletons = eagerInitSingletons_.rlock();
|
||
|
for (auto* single : *eagerInitSingletons) {
|
||
|
single->createInstance();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) {
|
||
|
{
|
||
|
auto state = state_.rlock();
|
||
|
state->check(detail::SingletonVaultState::Type::Running);
|
||
|
if (UNLIKELY(!state->registrationComplete)) {
|
||
|
throw std::logic_error("registrationComplete() not yet called");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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
|
||
|
// its values, which are SingletonHolderBase pointers, are alive as long as
|
||
|
// SingletonVault is not being destroyed.
|
||
|
exe.add([=] {
|
||
|
// decrement counter and notify if requested, whether initialization
|
||
|
// was successful, was skipped (already initialized), or exception thrown.
|
||
|
SCOPE_EXIT {
|
||
|
if (--(*countdown) == 0) {
|
||
|
if (done != nullptr) {
|
||
|
done->post();
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
// if initialization is in progress in another thread, don't try to init
|
||
|
// here. Otherwise the current thread will block on 'createInstance'.
|
||
|
if (!single->creationStarted()) {
|
||
|
single->createInstance();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SingletonVault::destroyInstances() {
|
||
|
auto stateW = state_.wlock();
|
||
|
if (stateW->state == detail::SingletonVaultState::Type::Quiescing) {
|
||
|
return;
|
||
|
}
|
||
|
stateW->state = detail::SingletonVaultState::Type::Quiescing;
|
||
|
|
||
|
auto stateR = stateW.moveFromWriteToRead();
|
||
|
{
|
||
|
auto singletons = singletons_.rlock();
|
||
|
auto creationOrder = creationOrder_.rlock();
|
||
|
|
||
|
CHECK_GE(singletons->size(), creationOrder->size());
|
||
|
|
||
|
// Release all ReadMostlyMainPtrs at once
|
||
|
{
|
||
|
ReadMostlyMainPtrDeleter<> deleter;
|
||
|
for (auto& singleton_type : *creationOrder) {
|
||
|
singletons->at(singleton_type)->preDestroyInstance(deleter);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (auto type_iter = creationOrder->rbegin();
|
||
|
type_iter != creationOrder->rend();
|
||
|
++type_iter) {
|
||
|
singletons->at(*type_iter)->destroyInstance();
|
||
|
}
|
||
|
|
||
|
for (auto& singleton_type : *creationOrder) {
|
||
|
auto instance = singletons->at(singleton_type);
|
||
|
if (!instance->hasLiveInstance()) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
fatalHelper.leakedSingletons_.push_back(instance->type());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
{
|
||
|
auto creationOrder = creationOrder_.wlock();
|
||
|
creationOrder->clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SingletonVault::reenableInstances() {
|
||
|
auto state = state_.wlock();
|
||
|
|
||
|
state->check(detail::SingletonVaultState::Type::Quiescing);
|
||
|
|
||
|
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, void>::instance();
|
||
|
std::atexit([] { SingletonVault::singleton()->destroyInstances(); });
|
||
|
}
|
||
|
|
||
|
} // namespace folly
|