Commit 4d897276 authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Sara Golemon

Revert "Revert "Using type-tags for test SingletonVaults""

Summary:
This reverts commit 4893c09795ad4b1187518b184ac4812079039988.

Fix unit test in D1823663.

Test Plan: fbconfig -r folly fbmake dbg

Reviewed By: alikhtarov@fb.com

Subscribers: folly-diffs@, yfeldblum

FB internal diff: D1832645

Signature: t1:1832645:1423267466:6012f1d7700d540c7290c29b01b33148cf91183c
parent 5569e6fa
...@@ -86,11 +86,6 @@ void SingletonVault::reenableInstances() { ...@@ -86,11 +86,6 @@ void SingletonVault::reenableInstances() {
state_ = SingletonVaultState::Running; state_ = SingletonVaultState::Running;
} }
SingletonVault* SingletonVault::singleton() {
static SingletonVault* vault = new SingletonVault();
return vault;
}
void SingletonVault::scheduleDestroyInstances() { void SingletonVault::scheduleDestroyInstances() {
RequestContext::getStaticContext(); RequestContext::getStaticContext();
......
...@@ -399,7 +399,17 @@ class SingletonVault { ...@@ -399,7 +399,17 @@ class SingletonVault {
// A well-known vault; you can actually have others, but this is the // A well-known vault; you can actually have others, but this is the
// default. // default.
static SingletonVault* singleton(); static SingletonVault* singleton() {
return singleton<>();
}
// Gets singleton vault for any Tag. Non-default tag should be used in unit
// tests only.
template <typename VaultTag = detail::DefaultTag>
static SingletonVault* singleton() {
static SingletonVault* vault = new SingletonVault();
return vault;
}
private: private:
// The two stages of life for a vault, as mentioned in the class comment. // The two stages of life for a vault, as mentioned in the class comment.
...@@ -534,7 +544,9 @@ class SingletonVault { ...@@ -534,7 +544,9 @@ class SingletonVault {
// singletons. Create instances of this class in the global scope of // singletons. Create instances of this class in the global scope of
// type Singleton<T> to register your singleton for later access via // type Singleton<T> to register your singleton for later access via
// Singleton<T>::get(). // Singleton<T>::get().
template <typename T, typename Tag = detail::DefaultTag> template <typename T,
typename Tag = detail::DefaultTag,
typename VaultTag = detail::DefaultTag /* for testing */>
class Singleton { class Singleton {
public: public:
typedef std::function<T*(void)> CreateFunc; typedef std::function<T*(void)> CreateFunc;
...@@ -543,9 +555,9 @@ class Singleton { ...@@ -543,9 +555,9 @@ class Singleton {
// Generally your program life cycle should be fine with calling // Generally your program life cycle should be fine with calling
// get() repeatedly rather than saving the reference, and then not // get() repeatedly rather than saving the reference, and then not
// call get() during process shutdown. // call get() during process shutdown.
static T* get(SingletonVault* vault = nullptr /* for testing */) { static T* get() {
return static_cast<T*>( return static_cast<T*>(
(vault ?: SingletonVault::singleton())->get_ptr(typeDescriptor())); SingletonVault::singleton<VaultTag>()->get_ptr(typeDescriptor()));
} }
// Same as get, but should be preffered to it in the same compilation // Same as get, but should be preffered to it in the same compilation
...@@ -554,7 +566,7 @@ class Singleton { ...@@ -554,7 +566,7 @@ class Singleton {
if (LIKELY(entry_->state == detail::SingletonEntryState::Living)) { if (LIKELY(entry_->state == detail::SingletonEntryState::Living)) {
return reinterpret_cast<T*>(entry_->instance_ptr); return reinterpret_cast<T*>(entry_->instance_ptr);
} else { } else {
return get(vault_); return get();
} }
} }
...@@ -562,10 +574,9 @@ class Singleton { ...@@ -562,10 +574,9 @@ class Singleton {
// singleton, you can try to do so with a weak_ptr. Avoid this when // singleton, you can try to do so with a weak_ptr. Avoid this when
// possible but the inability to lock the weak pointer can be a // possible but the inability to lock the weak pointer can be a
// signal that the vault has been destroyed. // signal that the vault has been destroyed.
static std::weak_ptr<T> get_weak( static std::weak_ptr<T> get_weak() {
SingletonVault* vault = nullptr /* for testing */) {
auto weak_void_ptr = auto weak_void_ptr =
(vault ?: SingletonVault::singleton())->get_weak(typeDescriptor()); (SingletonVault::singleton<VaultTag>())->get_weak(typeDescriptor());
// This is ugly and inefficient, but there's no other way to do it, because // This is ugly and inefficient, but there's no other way to do it, because
// there's no static_pointer_cast for weak_ptr. // there's no static_pointer_cast for weak_ptr.
...@@ -588,7 +599,7 @@ class Singleton { ...@@ -588,7 +599,7 @@ class Singleton {
} }
return std::static_pointer_cast<T>(shared_void_ptr); return std::static_pointer_cast<T>(shared_void_ptr);
} else { } else {
return get_weak(vault_); return get_weak();
} }
} }
...@@ -599,26 +610,19 @@ class Singleton { ...@@ -599,26 +610,19 @@ class Singleton {
T* operator->() { return ptr(); } T* operator->() { return ptr(); }
explicit Singleton(std::nullptr_t _ = nullptr, explicit Singleton(std::nullptr_t _ = nullptr,
Singleton::TeardownFunc t = nullptr, Singleton::TeardownFunc t = nullptr) :
SingletonVault* vault = nullptr) : Singleton ([]() { return new T; }, std::move(t)) {
Singleton ([]() { return new T; },
std::move(t),
vault) {
} }
explicit Singleton(Singleton::CreateFunc c, explicit Singleton(Singleton::CreateFunc c,
Singleton::TeardownFunc t = nullptr, Singleton::TeardownFunc t = nullptr) {
SingletonVault* vault = nullptr) {
if (c == nullptr) { if (c == nullptr) {
throw std::logic_error( throw std::logic_error(
"nullptr_t should be passed if you want T to be default constructed"); "nullptr_t should be passed if you want T to be default constructed");
} }
if (vault == nullptr) { auto vault = SingletonVault::singleton<VaultTag>();
vault = SingletonVault::singleton();
}
vault_ = vault;
entry_ = entry_ =
&(vault->registerSingleton(typeDescriptor(), c, getTeardownFunc(t))); &(vault->registerSingleton(typeDescriptor(), c, getTeardownFunc(t)));
} }
...@@ -634,22 +638,18 @@ class Singleton { ...@@ -634,22 +638,18 @@ class Singleton {
* regular singletons. * regular singletons.
*/ */
static void make_mock(std::nullptr_t c = nullptr, static void make_mock(std::nullptr_t c = nullptr,
typename Singleton<T>::TeardownFunc t = nullptr, typename Singleton<T>::TeardownFunc t = nullptr) {
SingletonVault* vault = nullptr /* for testing */ ) { make_mock([]() { return new T; }, t);
make_mock([]() { return new T; }, t, vault);
} }
static void make_mock(CreateFunc c, static void make_mock(CreateFunc c,
typename Singleton<T>::TeardownFunc t = nullptr, typename Singleton<T>::TeardownFunc t = nullptr) {
SingletonVault* vault = nullptr /* for testing */ ) {
if (c == nullptr) { if (c == nullptr) {
throw std::logic_error( throw std::logic_error(
"nullptr_t should be passed if you want T to be default constructed"); "nullptr_t should be passed if you want T to be default constructed");
} }
if (vault == nullptr) { auto vault = SingletonVault::singleton<VaultTag>();
vault = SingletonVault::singleton();
}
vault->registerMockSingleton( vault->registerMockSingleton(
typeDescriptor(), typeDescriptor(),
...@@ -680,7 +680,6 @@ class Singleton { ...@@ -680,7 +680,6 @@ class Singleton {
// We rely on the fact that Singleton destructor won't reset this pointer, so // We rely on the fact that Singleton destructor won't reset this pointer, so
// it can be "safely" used even after static Singleton object is destroyed. // it can be "safely" used even after static Singleton object is destroyed.
detail::SingletonEntry* entry_; detail::SingletonEntry* entry_;
SingletonVault* vault_;
}; };
} }
...@@ -83,30 +83,34 @@ TEST(Singleton, MissingSingleton) { ...@@ -83,30 +83,34 @@ TEST(Singleton, MissingSingleton) {
std::out_of_range); std::out_of_range);
} }
struct BasicUsageTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonBasicUsage = Singleton <T, Tag, BasicUsageTag>;
// Exercise some basic codepaths ensuring registration order and // Exercise some basic codepaths ensuring registration order and
// destruction order happen as expected, that instances are created // destruction order happen as expected, that instances are created
// when expected, etc etc. // when expected, etc etc.
TEST(Singleton, BasicUsage) { TEST(Singleton, BasicUsage) {
SingletonVault vault; auto& vault = *SingletonVault::singleton<BasicUsageTag>();
EXPECT_EQ(vault.registeredSingletonCount(), 0); EXPECT_EQ(vault.registeredSingletonCount(), 0);
Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault); SingletonBasicUsage<Watchdog> watchdog_singleton;
EXPECT_EQ(vault.registeredSingletonCount(), 1); EXPECT_EQ(vault.registeredSingletonCount(), 1);
Singleton<ChildWatchdog> child_watchdog_singleton(nullptr, nullptr, &vault); SingletonBasicUsage<ChildWatchdog> child_watchdog_singleton;
EXPECT_EQ(vault.registeredSingletonCount(), 2); EXPECT_EQ(vault.registeredSingletonCount(), 2);
vault.registrationComplete(); vault.registrationComplete();
Watchdog* s1 = Singleton<Watchdog>::get(&vault); Watchdog* s1 = SingletonBasicUsage<Watchdog>::get();
EXPECT_NE(s1, nullptr); EXPECT_NE(s1, nullptr);
Watchdog* s2 = Singleton<Watchdog>::get(&vault); Watchdog* s2 = SingletonBasicUsage<Watchdog>::get();
EXPECT_NE(s2, nullptr); EXPECT_NE(s2, nullptr);
EXPECT_EQ(s1, s2); EXPECT_EQ(s1, s2);
auto s3 = Singleton<ChildWatchdog>::get(&vault); auto s3 = SingletonBasicUsage<ChildWatchdog>::get();
EXPECT_NE(s3, nullptr); EXPECT_NE(s3, nullptr);
EXPECT_NE(s2, s3); EXPECT_NE(s2, s3);
...@@ -118,28 +122,38 @@ TEST(Singleton, BasicUsage) { ...@@ -118,28 +122,38 @@ TEST(Singleton, BasicUsage) {
EXPECT_EQ(vault.livingSingletonCount(), 0); EXPECT_EQ(vault.livingSingletonCount(), 0);
} }
struct DirectUsageTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonDirectUsage = Singleton <T, Tag, DirectUsageTag>;
TEST(Singleton, DirectUsage) { TEST(Singleton, DirectUsage) {
SingletonVault vault; auto& vault = *SingletonVault::singleton<DirectUsageTag>();
EXPECT_EQ(vault.registeredSingletonCount(), 0); EXPECT_EQ(vault.registeredSingletonCount(), 0);
// Verify we can get to the underlying singletons via directly using // Verify we can get to the underlying singletons via directly using
// the singleton definition. // the singleton definition.
Singleton<Watchdog> watchdog(nullptr, nullptr, &vault); SingletonDirectUsage<Watchdog> watchdog;
struct TestTag {}; struct TestTag {};
Singleton<Watchdog, TestTag> named_watchdog(nullptr, nullptr, &vault); SingletonDirectUsage<Watchdog, TestTag> named_watchdog;
EXPECT_EQ(vault.registeredSingletonCount(), 2); EXPECT_EQ(vault.registeredSingletonCount(), 2);
vault.registrationComplete(); vault.registrationComplete();
EXPECT_NE(watchdog.ptr(), nullptr); EXPECT_NE(watchdog.ptr(), nullptr);
EXPECT_EQ(watchdog.ptr(), Singleton<Watchdog>::get(&vault)); EXPECT_EQ(watchdog.ptr(), SingletonDirectUsage<Watchdog>::get());
EXPECT_NE(watchdog.ptr(), named_watchdog.ptr()); EXPECT_NE(watchdog.ptr(), named_watchdog.ptr());
EXPECT_EQ(watchdog->livingWatchdogCount(), 2); EXPECT_EQ(watchdog->livingWatchdogCount(), 2);
EXPECT_EQ((*watchdog).livingWatchdogCount(), 2); EXPECT_EQ((*watchdog).livingWatchdogCount(), 2);
vault.destroyInstances();
} }
struct NamedUsageTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonNamedUsage = Singleton <T, Tag, NamedUsageTag>;
TEST(Singleton, NamedUsage) { TEST(Singleton, NamedUsage) {
SingletonVault vault; auto& vault = *SingletonVault::singleton<NamedUsageTag>();
EXPECT_EQ(vault.registeredSingletonCount(), 0); EXPECT_EQ(vault.registeredSingletonCount(), 0);
...@@ -147,96 +161,105 @@ TEST(Singleton, NamedUsage) { ...@@ -147,96 +161,105 @@ TEST(Singleton, NamedUsage) {
struct Watchdog1 {}; struct Watchdog1 {};
struct Watchdog2 {}; struct Watchdog2 {};
typedef detail::DefaultTag Watchdog3; typedef detail::DefaultTag Watchdog3;
Singleton<Watchdog, Watchdog1> watchdog1_singleton(nullptr, nullptr, &vault); SingletonNamedUsage<Watchdog, Watchdog1> watchdog1_singleton;
EXPECT_EQ(vault.registeredSingletonCount(), 1); EXPECT_EQ(vault.registeredSingletonCount(), 1);
Singleton<Watchdog, Watchdog2> watchdog2_singleton(nullptr, nullptr, &vault); SingletonNamedUsage<Watchdog, Watchdog2> watchdog2_singleton;
EXPECT_EQ(vault.registeredSingletonCount(), 2); EXPECT_EQ(vault.registeredSingletonCount(), 2);
Singleton<Watchdog, Watchdog3> watchdog3_singleton(nullptr, nullptr, &vault); SingletonNamedUsage<Watchdog, Watchdog3> watchdog3_singleton;
EXPECT_EQ(vault.registeredSingletonCount(), 3); EXPECT_EQ(vault.registeredSingletonCount(), 3);
vault.registrationComplete(); vault.registrationComplete();
// Verify our three singletons are distinct and non-nullptr. // Verify our three singletons are distinct and non-nullptr.
Watchdog* s1 = Singleton<Watchdog, Watchdog1>::get(&vault); Watchdog* s1 = SingletonNamedUsage<Watchdog, Watchdog1>::get();
EXPECT_EQ(s1, watchdog1_singleton.ptr()); EXPECT_EQ(s1, watchdog1_singleton.ptr());
Watchdog* s2 = Singleton<Watchdog, Watchdog2>::get(&vault); Watchdog* s2 = SingletonNamedUsage<Watchdog, Watchdog2>::get();
EXPECT_EQ(s2, watchdog2_singleton.ptr()); EXPECT_EQ(s2, watchdog2_singleton.ptr());
EXPECT_NE(s1, s2); EXPECT_NE(s1, s2);
Watchdog* s3 = Singleton<Watchdog, Watchdog3>::get(&vault); Watchdog* s3 = SingletonNamedUsage<Watchdog, Watchdog3>::get();
EXPECT_EQ(s3, watchdog3_singleton.ptr()); EXPECT_EQ(s3, watchdog3_singleton.ptr());
EXPECT_NE(s3, s1); EXPECT_NE(s3, s1);
EXPECT_NE(s3, s2); EXPECT_NE(s3, s2);
// Verify the "default" singleton is the same as the DefaultTag-tagged // Verify the "default" singleton is the same as the DefaultTag-tagged
// singleton. // singleton.
Watchdog* s4 = Singleton<Watchdog>::get(&vault); Watchdog* s4 = SingletonNamedUsage<Watchdog>::get();
EXPECT_EQ(s4, watchdog3_singleton.ptr()); EXPECT_EQ(s4, watchdog3_singleton.ptr());
vault.destroyInstances();
} }
struct NaughtyUsageTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonNaughtyUsage = Singleton <T, Tag, NaughtyUsageTag>;
struct NaughtyUsageTag2 {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonNaughtyUsage2 = Singleton <T, Tag, NaughtyUsageTag2>;
// Some pathological cases such as getting unregistered singletons, // Some pathological cases such as getting unregistered singletons,
// double registration, etc. // double registration, etc.
TEST(Singleton, NaughtyUsage) { TEST(Singleton, NaughtyUsage) {
SingletonVault vault(SingletonVault::Type::Strict); auto& vault = *SingletonVault::singleton<NaughtyUsageTag>();
vault.registrationComplete(); vault.registrationComplete();
// Unregistered. // Unregistered.
EXPECT_THROW(Singleton<Watchdog>::get(), std::out_of_range); EXPECT_THROW(Singleton<Watchdog>::get(), std::out_of_range);
EXPECT_THROW(Singleton<Watchdog>::get(&vault), std::out_of_range); EXPECT_THROW(SingletonNaughtyUsage<Watchdog>::get(), std::out_of_range);
// Registring singletons after registrationComplete called. vault.destroyInstances();
EXPECT_THROW([&vault]() {
Singleton<Watchdog> watchdog_singleton( auto& vault2 = *SingletonVault::singleton<NaughtyUsageTag2>();
nullptr, nullptr, &vault);
}(),
std::logic_error);
SingletonVault vault_2(SingletonVault::Type::Strict); EXPECT_THROW(SingletonNaughtyUsage2<Watchdog>::get(), std::logic_error);
EXPECT_THROW(Singleton<Watchdog>::get(&vault_2), std::logic_error); SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault_2);
// double registration // double registration
EXPECT_THROW([&vault_2]() { EXPECT_THROW([]() {
Singleton<Watchdog> watchdog_singleton( SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
nullptr, nullptr, &vault_2);
}(), }(),
std::logic_error); std::logic_error);
vault_2.destroyInstances(); vault2.destroyInstances();
// double registration after destroy // double registration after destroy
EXPECT_THROW([&vault_2]() { EXPECT_THROW([]() {
Singleton<Watchdog> watchdog_singleton( SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
nullptr, nullptr, &vault_2);
}(), }(),
std::logic_error); std::logic_error);
} }
struct SharedPtrUsageTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonSharedPtrUsage = Singleton <T, Tag, SharedPtrUsageTag>;
TEST(Singleton, SharedPtrUsage) { TEST(Singleton, SharedPtrUsage) {
SingletonVault vault; auto& vault = *SingletonVault::singleton<SharedPtrUsageTag>();
EXPECT_EQ(vault.registeredSingletonCount(), 0); EXPECT_EQ(vault.registeredSingletonCount(), 0);
Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault); SingletonSharedPtrUsage<Watchdog> watchdog_singleton;
EXPECT_EQ(vault.registeredSingletonCount(), 1); EXPECT_EQ(vault.registeredSingletonCount(), 1);
Singleton<ChildWatchdog> child_watchdog_singleton(nullptr, nullptr, &vault); SingletonSharedPtrUsage<ChildWatchdog> child_watchdog_singleton;
EXPECT_EQ(vault.registeredSingletonCount(), 2); EXPECT_EQ(vault.registeredSingletonCount(), 2);
struct ATag {}; struct ATag {};
Singleton<Watchdog, ATag> named_watchdog_singleton(nullptr, nullptr, &vault); SingletonSharedPtrUsage<Watchdog, ATag> named_watchdog_singleton;
vault.registrationComplete(); vault.registrationComplete();
Watchdog* s1 = Singleton<Watchdog>::get(&vault); Watchdog* s1 = SingletonSharedPtrUsage<Watchdog>::get();
EXPECT_NE(s1, nullptr); EXPECT_NE(s1, nullptr);
Watchdog* s2 = Singleton<Watchdog>::get(&vault); Watchdog* s2 = SingletonSharedPtrUsage<Watchdog>::get();
EXPECT_NE(s2, nullptr); EXPECT_NE(s2, nullptr);
EXPECT_EQ(s1, s2); EXPECT_EQ(s1, s2);
auto weak_s1 = Singleton<Watchdog>::get_weak(&vault); 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 named_weak_s1 = Singleton<Watchdog, ATag>::get_weak(&vault); auto named_weak_s1 =
SingletonSharedPtrUsage<Watchdog, ATag>::get_weak();
auto locked = named_weak_s1.lock(); auto locked = named_weak_s1.lock();
EXPECT_NE(locked.get(), shared_s1.get()); EXPECT_NE(locked.get(), shared_s1.get());
} }
...@@ -268,16 +291,16 @@ TEST(Singleton, SharedPtrUsage) { ...@@ -268,16 +291,16 @@ TEST(Singleton, SharedPtrUsage) {
locked_s1 = weak_s1.lock(); locked_s1 = weak_s1.lock();
EXPECT_TRUE(weak_s1.expired()); EXPECT_TRUE(weak_s1.expired());
auto empty_s1 = Singleton<Watchdog>::get_weak(&vault); auto empty_s1 = SingletonSharedPtrUsage<Watchdog>::get_weak();
EXPECT_FALSE(empty_s1.lock()); EXPECT_FALSE(empty_s1.lock());
vault.reenableInstances(); vault.reenableInstances();
// Singleton should be re-created only after reenableInstances() was called. // Singleton should be re-created only after reenableInstances() was called.
Watchdog* new_s1 = Singleton<Watchdog>::get(&vault); Watchdog* new_s1 = SingletonSharedPtrUsage<Watchdog>::get();
EXPECT_NE(new_s1->serial_number, old_serial); EXPECT_NE(new_s1->serial_number, old_serial);
auto new_s1_weak = Singleton<Watchdog>::get_weak(&vault); auto new_s1_weak = SingletonSharedPtrUsage<Watchdog>::get_weak();
auto new_s1_shared = new_s1_weak.lock(); auto new_s1_shared = new_s1_weak.lock();
std::thread t([new_s1_shared]() mutable { std::thread t([new_s1_shared]() mutable {
std::this_thread::sleep_for(std::chrono::seconds{2}); std::this_thread::sleep_for(std::chrono::seconds{2});
...@@ -298,41 +321,49 @@ TEST(Singleton, SharedPtrUsage) { ...@@ -298,41 +321,49 @@ TEST(Singleton, SharedPtrUsage) {
// Some classes to test singleton dependencies. NeedySingleton has a // Some classes to test singleton dependencies. NeedySingleton has a
// dependency on NeededSingleton, which happens during its // dependency on NeededSingleton, which happens during its
// construction. // construction.
SingletonVault needy_vault; struct NeedyTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonNeedy = Singleton <T, Tag, NeedyTag>;
struct NeededSingleton {}; struct NeededSingleton {};
struct NeedySingleton { struct NeedySingleton {
NeedySingleton() { NeedySingleton() {
auto unused = Singleton<NeededSingleton>::get(&needy_vault); auto unused = SingletonNeedy<NeededSingleton>::get();
EXPECT_NE(unused, nullptr); EXPECT_NE(unused, nullptr);
} }
}; };
// Ensure circular dependencies fail -- a singleton that needs itself, whoops. // Ensure circular dependencies fail -- a singleton that needs itself, whoops.
SingletonVault self_needy_vault; struct SelfNeedyTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonSelfNeedy = Singleton <T, Tag, SelfNeedyTag>;
struct SelfNeedySingleton { struct SelfNeedySingleton {
SelfNeedySingleton() { SelfNeedySingleton() {
auto unused = Singleton<SelfNeedySingleton>::get(&self_needy_vault); auto unused = SingletonSelfNeedy<SelfNeedySingleton>::get();
EXPECT_NE(unused, nullptr); EXPECT_NE(unused, nullptr);
} }
}; };
TEST(Singleton, SingletonDependencies) { TEST(Singleton, SingletonDependencies) {
Singleton<NeededSingleton> needed_singleton(nullptr, nullptr, &needy_vault); SingletonNeedy<NeededSingleton> needed_singleton;
Singleton<NeedySingleton> needy_singleton(nullptr, nullptr, &needy_vault); SingletonNeedy<NeedySingleton> needy_singleton;
auto& needy_vault = *SingletonVault::singleton<NeedyTag>();
needy_vault.registrationComplete(); needy_vault.registrationComplete();
EXPECT_EQ(needy_vault.registeredSingletonCount(), 2); EXPECT_EQ(needy_vault.registeredSingletonCount(), 2);
EXPECT_EQ(needy_vault.livingSingletonCount(), 0); EXPECT_EQ(needy_vault.livingSingletonCount(), 0);
auto needy = Singleton<NeedySingleton>::get(&needy_vault); auto needy = SingletonNeedy<NeedySingleton>::get();
EXPECT_EQ(needy_vault.livingSingletonCount(), 2); EXPECT_EQ(needy_vault.livingSingletonCount(), 2);
Singleton<SelfNeedySingleton> self_needy_singleton( SingletonSelfNeedy<SelfNeedySingleton> self_needy_singleton;
nullptr, nullptr, &self_needy_vault); auto& self_needy_vault = *SingletonVault::singleton<SelfNeedyTag>();
self_needy_vault.registrationComplete(); self_needy_vault.registrationComplete();
EXPECT_THROW([]() { EXPECT_THROW([]() {
Singleton<SelfNeedySingleton>::get(&self_needy_vault); SingletonSelfNeedy<SelfNeedySingleton>::get();
}(), }(),
std::out_of_range); std::out_of_range);
} }
...@@ -345,17 +376,21 @@ class Slowpoke : public Watchdog { ...@@ -345,17 +376,21 @@ class Slowpoke : public Watchdog {
Slowpoke() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } Slowpoke() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); }
}; };
struct ConcurrencyTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonConcurrency = Singleton <T, Tag, ConcurrencyTag>;
TEST(Singleton, SingletonConcurrency) { TEST(Singleton, SingletonConcurrency) {
SingletonVault vault; auto& vault = *SingletonVault::singleton<ConcurrencyTag>();
Singleton<Slowpoke> slowpoke_singleton(nullptr, nullptr, &vault); SingletonConcurrency<Slowpoke> slowpoke_singleton;
vault.registrationComplete(); vault.registrationComplete();
std::mutex gatekeeper; std::mutex gatekeeper;
gatekeeper.lock(); gatekeeper.lock();
auto func = [&vault, &gatekeeper]() { auto func = [&gatekeeper]() {
gatekeeper.lock(); gatekeeper.lock();
gatekeeper.unlock(); gatekeeper.unlock();
auto unused = Singleton<Slowpoke>::get(&vault); auto unused = SingletonConcurrency<Slowpoke>::get();
}; };
EXPECT_EQ(vault.livingSingletonCount(), 0); EXPECT_EQ(vault.livingSingletonCount(), 0);
...@@ -373,14 +408,18 @@ TEST(Singleton, SingletonConcurrency) { ...@@ -373,14 +408,18 @@ TEST(Singleton, SingletonConcurrency) {
EXPECT_EQ(vault.livingSingletonCount(), 1); EXPECT_EQ(vault.livingSingletonCount(), 1);
} }
struct ConcurrencyStressTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonConcurrencyStress = Singleton <T, Tag, ConcurrencyStressTag>;
TEST(Singleton, SingletonConcurrencyStress) { TEST(Singleton, SingletonConcurrencyStress) {
SingletonVault vault; auto& vault = *SingletonVault::singleton<ConcurrencyStressTag>();
Singleton<Slowpoke> slowpoke_singleton(nullptr, nullptr, &vault); SingletonConcurrencyStress<Slowpoke> slowpoke_singleton;
std::vector<std::thread> ts; std::vector<std::thread> ts;
for (size_t i = 0; i < 100; ++i) { for (size_t i = 0; i < 100; ++i) {
ts.emplace_back([&]() { ts.emplace_back([&]() {
slowpoke_singleton.get_weak(&vault).lock(); slowpoke_singleton.get_weak().lock();
}); });
} }
...@@ -412,23 +451,28 @@ int* getNormalSingleton() { ...@@ -412,23 +451,28 @@ int* getNormalSingleton() {
return &normal_singleton_value; return &normal_singleton_value;
} }
struct MockTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonMock = Singleton <T, Tag, MockTag>;
// Verify that existing Singleton's can be overridden // Verify that existing Singleton's can be overridden
// using the make_mock functionality. // using the make_mock functionality.
TEST(Singleton, make_mock) { TEST(Singleton, MockTest) {
SingletonVault vault(SingletonVault::Type::Strict); auto& vault = *SingletonVault::singleton<MockTag>();
Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault);
SingletonMock<Watchdog> watchdog_singleton;
vault.registrationComplete(); vault.registrationComplete();
// Registring singletons after registrationComplete called works // Registring singletons after registrationComplete called works
// with make_mock (but not with Singleton ctor). // with make_mock (but not with Singleton ctor).
EXPECT_EQ(vault.registeredSingletonCount(), 1); EXPECT_EQ(vault.registeredSingletonCount(), 1);
int serial_count_first = Singleton<Watchdog>::get(&vault)->serial_number; int serial_count_first = SingletonMock<Watchdog>::get()->serial_number;
// Override existing mock using make_mock. // Override existing mock using make_mock.
Singleton<Watchdog>::make_mock(nullptr, nullptr, &vault); SingletonMock<Watchdog>::make_mock();
EXPECT_EQ(vault.registeredSingletonCount(), 1); EXPECT_EQ(vault.registeredSingletonCount(), 1);
int serial_count_mock = Singleton<Watchdog>::get(&vault)->serial_number; int serial_count_mock = SingletonMock<Watchdog>::get()->serial_number;
// If serial_count value is the same, then singleton was not replaced. // If serial_count value is the same, then singleton was not replaced.
EXPECT_NE(serial_count_first, serial_count_mock); EXPECT_NE(serial_count_first, serial_count_mock);
...@@ -450,34 +494,25 @@ BENCHMARK_RELATIVE(MeyersSingleton, n) { ...@@ -450,34 +494,25 @@ BENCHMARK_RELATIVE(MeyersSingleton, n) {
} }
} }
BENCHMARK_RELATIVE(FollySingletonSlow, n) { struct BenchmarkTag {};
SingletonVault benchmark_vault; template <typename T, typename Tag = detail::DefaultTag>
Singleton<BenchmarkSingleton> benchmark_singleton( using SingletonBenchmark = Singleton <T, Tag, BenchmarkTag>;
nullptr, nullptr, &benchmark_vault);
benchmark_vault.registrationComplete(); SingletonBenchmark<BenchmarkSingleton> benchmark_singleton;
BENCHMARK_RELATIVE(FollySingletonSlow, n) {
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
doNotOptimizeAway(Singleton<BenchmarkSingleton>::get(&benchmark_vault)); doNotOptimizeAway(SingletonBenchmark<BenchmarkSingleton>::get());
} }
} }
BENCHMARK_RELATIVE(FollySingletonFast, n) { BENCHMARK_RELATIVE(FollySingletonFast, n) {
SingletonVault benchmark_vault;
Singleton<BenchmarkSingleton> benchmark_singleton(
nullptr, nullptr, &benchmark_vault);
benchmark_vault.registrationComplete();
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
doNotOptimizeAway(benchmark_singleton.get_fast()); doNotOptimizeAway(benchmark_singleton.get_fast());
} }
} }
BENCHMARK_RELATIVE(FollySingletonFastWeak, n) { BENCHMARK_RELATIVE(FollySingletonFastWeak, n) {
SingletonVault benchmark_vault;
Singleton<BenchmarkSingleton> benchmark_singleton(
nullptr, nullptr, &benchmark_vault);
benchmark_vault.registrationComplete();
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
benchmark_singleton.get_weak_fast(); benchmark_singleton.get_weak_fast();
} }
......
...@@ -40,12 +40,16 @@ TEST(SingletonVault, singletonReturnsSingletonInstance) { ...@@ -40,12 +40,16 @@ TEST(SingletonVault, singletonReturnsSingletonInstance) {
EXPECT_EQ(c, cpp); EXPECT_EQ(c, cpp);
} }
struct TestTag {};
template <typename T, typename Tag = folly::detail::DefaultTag>
using SingletonTest = folly::Singleton <T, Tag, TestTag>;
TEST(SingletonVault, singletonsAreCreatedAndDestroyed) { TEST(SingletonVault, singletonsAreCreatedAndDestroyed) {
auto *vault = new folly::SingletonVault(); auto vault = folly::SingletonVault::singleton<TestTag>();
folly::Singleton<InstanceCounter> counter_singleton(nullptr, nullptr, vault); SingletonTest<InstanceCounter> counter_singleton;
SingletonVault_registrationComplete((SingletonVault_t*) vault); SingletonVault_registrationComplete((SingletonVault_t*) vault);
InstanceCounter *counter = folly::Singleton<InstanceCounter>::get(vault); InstanceCounter *counter = SingletonTest<InstanceCounter>::get();
EXPECT_EQ(instance_counter_instances, 1); EXPECT_EQ(instance_counter_instances, 1);
delete vault; SingletonVault_destroyInstances((SingletonVault_t*) vault);
EXPECT_EQ(instance_counter_instances, 0); EXPECT_EQ(instance_counter_instances, 0);
} }
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