Commit 5f2d43af authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Alecs King

Fix folly::Singleton DFATAL

Summary:
Previously leak-fatal could never happen because we were incorrectly checking singleton state.
Sending this diff to see how many tests will actually fail and potentially fix worst offenders.

Test Plan: unit tests

Reviewed By: stepan@fb.com

Subscribers: trunkagent, folly-diffs@, yfeldblum

FB internal diff: D1838405

Signature: t1:1838405:1424478983:94cda86ed57f38b0cf626b74804fbc168d182c66
parent 2f3b69cc
...@@ -85,7 +85,7 @@ TypeDescriptor SingletonHolder<T>::type() { ...@@ -85,7 +85,7 @@ TypeDescriptor SingletonHolder<T>::type() {
template <typename T> template <typename T>
bool SingletonHolder<T>::hasLiveInstance() { bool SingletonHolder<T>::hasLiveInstance() {
return state_ == SingletonHolderState::Living; return !instance_weak_.expired();
} }
template <typename T> template <typename T>
......
...@@ -231,6 +231,16 @@ template <typename T, typename Tag = detail::DefaultTag> ...@@ -231,6 +231,16 @@ template <typename T, typename Tag = detail::DefaultTag>
using SingletonSharedPtrUsage = Singleton <T, Tag, SharedPtrUsageTag>; using SingletonSharedPtrUsage = Singleton <T, Tag, SharedPtrUsageTag>;
TEST(Singleton, SharedPtrUsage) { TEST(Singleton, SharedPtrUsage) {
struct WatchdogHolder {
~WatchdogHolder() {
if (watchdog) {
LOG(ERROR) << "The following log message with stack trace is expected";
}
}
std::shared_ptr<Watchdog> watchdog;
};
auto& vault = *SingletonVault::singleton<SharedPtrUsageTag>(); auto& vault = *SingletonVault::singleton<SharedPtrUsageTag>();
EXPECT_EQ(vault.registeredSingletonCount(), 0); EXPECT_EQ(vault.registeredSingletonCount(), 0);
...@@ -242,8 +252,15 @@ TEST(Singleton, SharedPtrUsage) { ...@@ -242,8 +252,15 @@ TEST(Singleton, SharedPtrUsage) {
struct ATag {}; struct ATag {};
SingletonSharedPtrUsage<Watchdog, ATag> named_watchdog_singleton; SingletonSharedPtrUsage<Watchdog, ATag> named_watchdog_singleton;
SingletonSharedPtrUsage<WatchdogHolder> watchdog_holder_singleton;
vault.registrationComplete(); vault.registrationComplete();
// Initilize holder singleton first, so that it's the last one to be
// destroyed.
watchdog_holder_singleton.get();
Watchdog* s1 = SingletonSharedPtrUsage<Watchdog>::get(); Watchdog* s1 = SingletonSharedPtrUsage<Watchdog>::get();
EXPECT_NE(s1, nullptr); EXPECT_NE(s1, nullptr);
...@@ -253,10 +270,13 @@ TEST(Singleton, SharedPtrUsage) { ...@@ -253,10 +270,13 @@ TEST(Singleton, SharedPtrUsage) {
EXPECT_EQ(s1, s2); EXPECT_EQ(s1, s2);
auto weak_s1 = SingletonSharedPtrUsage<Watchdog>::get_weak(); auto weak_s1 = SingletonSharedPtrUsage<Watchdog>::get_weak();
auto shared_s1 = weak_s1.lock(); auto shared_s1 = weak_s1.lock();
EXPECT_EQ(shared_s1.get(), s1); EXPECT_EQ(shared_s1.get(), s1);
EXPECT_EQ(shared_s1.use_count(), 2); EXPECT_EQ(shared_s1.use_count(), 2);
auto old_serial = shared_s1->serial_number;
{ {
auto named_weak_s1 = auto named_weak_s1 =
SingletonSharedPtrUsage<Watchdog, ATag>::get_weak(); SingletonSharedPtrUsage<Watchdog, ATag>::get_weak();
...@@ -264,6 +284,10 @@ TEST(Singleton, SharedPtrUsage) { ...@@ -264,6 +284,10 @@ TEST(Singleton, SharedPtrUsage) {
EXPECT_NE(locked.get(), shared_s1.get()); EXPECT_NE(locked.get(), shared_s1.get());
} }
// We should release externally locked shared_ptr, otherwise it will be
// considered a leak
watchdog_holder_singleton->watchdog = std::move(shared_s1);
LOG(ERROR) << "The following log message regarding shared_ptr is expected"; LOG(ERROR) << "The following log message regarding shared_ptr is expected";
{ {
auto start_time = std::chrono::steady_clock::now(); auto start_time = std::chrono::steady_clock::now();
...@@ -272,24 +296,9 @@ TEST(Singleton, SharedPtrUsage) { ...@@ -272,24 +296,9 @@ TEST(Singleton, SharedPtrUsage) {
EXPECT_TRUE(duration > std::chrono::seconds{4} && EXPECT_TRUE(duration > std::chrono::seconds{4} &&
duration < std::chrono::seconds{6}); duration < std::chrono::seconds{6});
} }
EXPECT_EQ(vault.registeredSingletonCount(), 3); EXPECT_EQ(vault.registeredSingletonCount(), 4);
EXPECT_EQ(vault.livingSingletonCount(), 0); EXPECT_EQ(vault.livingSingletonCount(), 0);
EXPECT_EQ(shared_s1.use_count(), 1);
EXPECT_EQ(shared_s1.get(), s1);
auto locked_s1 = weak_s1.lock();
EXPECT_EQ(locked_s1.get(), s1);
EXPECT_EQ(shared_s1.use_count(), 2);
LOG(ERROR) << "The following log message with stack trace is expected";
locked_s1.reset();
EXPECT_EQ(shared_s1.use_count(), 1);
// Track serial number rather than pointer since the memory could be
// re-used when we create new_s1.
auto old_serial = shared_s1->serial_number;
shared_s1.reset();
locked_s1 = weak_s1.lock();
EXPECT_TRUE(weak_s1.expired()); EXPECT_TRUE(weak_s1.expired());
auto empty_s1 = SingletonSharedPtrUsage<Watchdog>::get_weak(); auto empty_s1 = SingletonSharedPtrUsage<Watchdog>::get_weak();
...@@ -299,6 +308,8 @@ TEST(Singleton, SharedPtrUsage) { ...@@ -299,6 +308,8 @@ TEST(Singleton, SharedPtrUsage) {
// Singleton should be re-created only after reenableInstances() was called. // Singleton should be re-created only after reenableInstances() was called.
Watchdog* new_s1 = SingletonSharedPtrUsage<Watchdog>::get(); Watchdog* new_s1 = SingletonSharedPtrUsage<Watchdog>::get();
// Track serial number rather than pointer since the memory could be
// re-used when we create new_s1.
EXPECT_NE(new_s1->serial_number, old_serial); EXPECT_NE(new_s1->serial_number, old_serial);
auto new_s1_weak = SingletonSharedPtrUsage<Watchdog>::get_weak(); auto new_s1_weak = SingletonSharedPtrUsage<Watchdog>::get_weak();
......
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