Commit 6caa6956 authored by Steve O'Brien's avatar Steve O'Brien Committed by Sara Golemon

folly/singleton: fatal in unrecoverable error cases

Summary: Early in the startup process there may not be a default signal handler installed, and stack traces are not available; also during the startup process is when init-order fiascos occur.  Dump a stacktrace and fatal when an unregistered singleton is used.

Also fatals -- with glog `LOG(FATAL)`, which triggers an ABRT signal -- on other illegal and unrecoverable usage like double-registration or circular dependency.

Reviewed By: @andriigrynenko

Differential Revision: D2200408
parent bf52653d
...@@ -32,7 +32,7 @@ void SingletonHolder<T>::registerSingleton(CreateFunc c, TeardownFunc t) { ...@@ -32,7 +32,7 @@ void SingletonHolder<T>::registerSingleton(CreateFunc c, TeardownFunc t) {
std::lock_guard<std::mutex> entry_lock(mutex_); std::lock_guard<std::mutex> entry_lock(mutex_);
if (state_ != SingletonHolderState::NotRegistered) { if (state_ != SingletonHolderState::NotRegistered) {
throw std::logic_error("Double registration"); LOG(FATAL) << "Double registration of singleton: " << type_.name();
} }
create_ = std::move(c); create_ = std::move(c);
...@@ -44,7 +44,8 @@ void SingletonHolder<T>::registerSingleton(CreateFunc c, TeardownFunc t) { ...@@ -44,7 +44,8 @@ void SingletonHolder<T>::registerSingleton(CreateFunc c, TeardownFunc t) {
template <typename T> template <typename T>
void SingletonHolder<T>::registerSingletonMock(CreateFunc c, TeardownFunc t) { void SingletonHolder<T>::registerSingletonMock(CreateFunc c, TeardownFunc t) {
if (state_ == SingletonHolderState::NotRegistered) { if (state_ == SingletonHolderState::NotRegistered) {
throw std::logic_error("Registering mock before singleton was registered"); LOG(FATAL)
<< "Registering mock before singleton was registered: " << type_.name();
} }
destroyInstance(); destroyInstance();
...@@ -119,8 +120,7 @@ void SingletonHolder<T>::createInstance() { ...@@ -119,8 +120,7 @@ void SingletonHolder<T>::createInstance() {
// for creating_thread if it was set by other thread, but we only care about // for creating_thread if it was set by other thread, but we only care about
// it if it was set by current thread anyways. // it if it was set by current thread anyways.
if (creating_thread_ == std::this_thread::get_id()) { if (creating_thread_ == std::this_thread::get_id()) {
throw std::out_of_range(std::string("circular singleton dependency: ") + LOG(FATAL) << "circular singleton dependency: " << type_.name();
type_.name());
} }
std::lock_guard<std::mutex> entry_lock(mutex_); std::lock_guard<std::mutex> entry_lock(mutex_);
...@@ -128,7 +128,11 @@ void SingletonHolder<T>::createInstance() { ...@@ -128,7 +128,11 @@ void SingletonHolder<T>::createInstance() {
return; return;
} }
if (state_ == SingletonHolderState::NotRegistered) { if (state_ == SingletonHolderState::NotRegistered) {
throw std::out_of_range("Creating instance for unregistered singleton"); auto ptr = SingletonVault::stackTraceGetter().load();
LOG(FATAL) << "Creating instance for unregistered singleton: "
<< type_.name() << "\n"
<< "Stacktrace:"
<< "\n" << (ptr ? (*ptr)() : "(not available)");
} }
if (state_ == SingletonHolderState::Living) { if (state_ == SingletonHolderState::Living) {
......
...@@ -46,8 +46,12 @@ struct SetStackTraceGetter { ...@@ -46,8 +46,12 @@ struct SetStackTraceGetter {
} }
}; };
#ifdef __APPLE__
// OS X doesn't support constructor priorities.
SetStackTraceGetter setStackTraceGetter; SetStackTraceGetter setStackTraceGetter;
#else
SetStackTraceGetter __attribute__((__init_priority__(101))) setStackTraceGetter;
#endif
} }
} }
...@@ -79,8 +79,7 @@ TEST(Singleton, BasicGlobalUsage) { ...@@ -79,8 +79,7 @@ TEST(Singleton, BasicGlobalUsage) {
} }
TEST(Singleton, MissingSingleton) { TEST(Singleton, MissingSingleton) {
EXPECT_THROW([]() { auto u = Singleton<UnregisteredWatchdog>::get(); }(), EXPECT_DEATH([]() { auto u = Singleton<UnregisteredWatchdog>::get(); }(), "");
std::out_of_range);
} }
struct BasicUsageTag {}; struct BasicUsageTag {};
...@@ -204,26 +203,24 @@ TEST(Singleton, NaughtyUsage) { ...@@ -204,26 +203,24 @@ TEST(Singleton, NaughtyUsage) {
vault.registrationComplete(); vault.registrationComplete();
// Unregistered. // Unregistered.
EXPECT_THROW(Singleton<Watchdog>::get(), std::out_of_range); EXPECT_DEATH(Singleton<Watchdog>::get(), "");
EXPECT_THROW(SingletonNaughtyUsage<Watchdog>::get(), std::out_of_range); EXPECT_DEATH(SingletonNaughtyUsage<Watchdog>::get(), "");
vault.destroyInstances(); vault.destroyInstances();
auto& vault2 = *SingletonVault::singleton<NaughtyUsageTag2>(); auto& vault2 = *SingletonVault::singleton<NaughtyUsageTag2>();
EXPECT_THROW(SingletonNaughtyUsage2<Watchdog>::get(), std::logic_error); EXPECT_DEATH(SingletonNaughtyUsage2<Watchdog>::get(), "");
SingletonNaughtyUsage2<Watchdog> watchdog_singleton; SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
// double registration // double registration
EXPECT_THROW([]() { EXPECT_DEATH([]() { SingletonNaughtyUsage2<Watchdog> watchdog_singleton; }(),
SingletonNaughtyUsage2<Watchdog> watchdog_singleton; "");
}(),
std::logic_error);
vault2.destroyInstances(); vault2.destroyInstances();
// double registration after destroy // double registration after destroy
EXPECT_THROW([]() { EXPECT_DEATH([]() { SingletonNaughtyUsage2<Watchdog> watchdog_singleton; }(),
SingletonNaughtyUsage2<Watchdog> watchdog_singleton; "");
}(),
std::logic_error);
} }
struct SharedPtrUsageTag {}; struct SharedPtrUsageTag {};
...@@ -374,10 +371,7 @@ TEST(Singleton, SingletonDependencies) { ...@@ -374,10 +371,7 @@ TEST(Singleton, SingletonDependencies) {
auto& self_needy_vault = *SingletonVault::singleton<SelfNeedyTag>(); auto& self_needy_vault = *SingletonVault::singleton<SelfNeedyTag>();
self_needy_vault.registrationComplete(); self_needy_vault.registrationComplete();
EXPECT_THROW([]() { EXPECT_DEATH([]() { SingletonSelfNeedy<SelfNeedySingleton>::get(); }(), "");
SingletonSelfNeedy<SelfNeedySingleton>::get();
}(),
std::out_of_range);
} }
// A test to ensure multiple threads contending on singleton creation // A test to ensure multiple threads contending on singleton creation
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment