// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <stdint.h> #include "base/at_exit.h" #include "base/memory/singleton.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { namespace { static_assert(DefaultSingletonTraits<int>::kRegisterAtExit == true, "object must be deleted on process exit"); typedef void (*CallbackFunc)(); template <size_t alignment> class AlignedData { public: AlignedData() = default; ~AlignedData() = default; alignas(alignment) char data_[alignment]; }; class IntSingleton { public: static IntSingleton* GetInstance() { return Singleton<IntSingleton>::get(); } int value_; }; class Init5Singleton { public: struct Trait; static Init5Singleton* GetInstance() { return Singleton<Init5Singleton, Trait>::get(); } int value_; }; struct Init5Singleton::Trait : public DefaultSingletonTraits<Init5Singleton> { static Init5Singleton* New() { Init5Singleton* instance = new Init5Singleton(); instance->value_ = 5; return instance; } }; int* SingletonInt() { return &IntSingleton::GetInstance()->value_; } int* SingletonInt5() { return &Init5Singleton::GetInstance()->value_; } template <typename Type> struct CallbackTrait : public DefaultSingletonTraits<Type> { static void Delete(Type* instance) { if (instance->callback_) (instance->callback_)(); DefaultSingletonTraits<Type>::Delete(instance); } }; class CallbackSingleton { public: CallbackSingleton() : callback_(nullptr) {} CallbackFunc callback_; }; class CallbackSingletonWithNoLeakTrait : public CallbackSingleton { public: struct Trait : public CallbackTrait<CallbackSingletonWithNoLeakTrait> { }; CallbackSingletonWithNoLeakTrait() : CallbackSingleton() { } static CallbackSingletonWithNoLeakTrait* GetInstance() { return Singleton<CallbackSingletonWithNoLeakTrait, Trait>::get(); } }; class CallbackSingletonWithLeakTrait : public CallbackSingleton { public: struct Trait : public CallbackTrait<CallbackSingletonWithLeakTrait> { static const bool kRegisterAtExit = false; }; CallbackSingletonWithLeakTrait() : CallbackSingleton() { } static CallbackSingletonWithLeakTrait* GetInstance() { return Singleton<CallbackSingletonWithLeakTrait, Trait>::get(); } }; class CallbackSingletonWithStaticTrait : public CallbackSingleton { public: struct Trait; CallbackSingletonWithStaticTrait() : CallbackSingleton() { } static CallbackSingletonWithStaticTrait* GetInstance() { return Singleton<CallbackSingletonWithStaticTrait, Trait>::get(); } }; struct CallbackSingletonWithStaticTrait::Trait : public StaticMemorySingletonTraits<CallbackSingletonWithStaticTrait> { static void Delete(CallbackSingletonWithStaticTrait* instance) { if (instance->callback_) (instance->callback_)(); StaticMemorySingletonTraits<CallbackSingletonWithStaticTrait>::Delete( instance); } }; template <class Type> class AlignedTestSingleton { public: AlignedTestSingleton() = default; ~AlignedTestSingleton() = default; static AlignedTestSingleton* GetInstance() { return Singleton<AlignedTestSingleton, StaticMemorySingletonTraits<AlignedTestSingleton>>::get(); } Type type_; }; void SingletonNoLeak(CallbackFunc CallOnQuit) { CallbackSingletonWithNoLeakTrait::GetInstance()->callback_ = CallOnQuit; } void SingletonLeak(CallbackFunc CallOnQuit) { CallbackSingletonWithLeakTrait::GetInstance()->callback_ = CallOnQuit; } CallbackFunc* GetLeakySingleton() { return &CallbackSingletonWithLeakTrait::GetInstance()->callback_; } void DeleteLeakySingleton() { DefaultSingletonTraits<CallbackSingletonWithLeakTrait>::Delete( CallbackSingletonWithLeakTrait::GetInstance()); } void SingletonStatic(CallbackFunc CallOnQuit) { CallbackSingletonWithStaticTrait::GetInstance()->callback_ = CallOnQuit; } CallbackFunc* GetStaticSingleton() { return &CallbackSingletonWithStaticTrait::GetInstance()->callback_; } class SingletonTest : public testing::Test { public: SingletonTest() = default; void SetUp() override { non_leak_called_ = false; leaky_called_ = false; static_called_ = false; } protected: void VerifiesCallbacks() { EXPECT_TRUE(non_leak_called_); EXPECT_FALSE(leaky_called_); EXPECT_TRUE(static_called_); non_leak_called_ = false; leaky_called_ = false; static_called_ = false; } void VerifiesCallbacksNotCalled() { EXPECT_FALSE(non_leak_called_); EXPECT_FALSE(leaky_called_); EXPECT_FALSE(static_called_); non_leak_called_ = false; leaky_called_ = false; static_called_ = false; } static void CallbackNoLeak() { non_leak_called_ = true; } static void CallbackLeak() { leaky_called_ = true; } static void CallbackStatic() { static_called_ = true; } private: static bool non_leak_called_; static bool leaky_called_; static bool static_called_; }; bool SingletonTest::non_leak_called_ = false; bool SingletonTest::leaky_called_ = false; bool SingletonTest::static_called_ = false; TEST_F(SingletonTest, Basic) { int* singleton_int; int* singleton_int_5; CallbackFunc* leaky_singleton; CallbackFunc* static_singleton; { ShadowingAtExitManager sem; { singleton_int = SingletonInt(); } // Ensure POD type initialization. EXPECT_EQ(*singleton_int, 0); *singleton_int = 1; EXPECT_EQ(singleton_int, SingletonInt()); EXPECT_EQ(*singleton_int, 1); { singleton_int_5 = SingletonInt5(); } // Is default initialized to 5. EXPECT_EQ(*singleton_int_5, 5); SingletonNoLeak(&CallbackNoLeak); SingletonLeak(&CallbackLeak); SingletonStatic(&CallbackStatic); static_singleton = GetStaticSingleton(); leaky_singleton = GetLeakySingleton(); EXPECT_TRUE(leaky_singleton); } // Verify that only the expected callback has been called. VerifiesCallbacks(); // Delete the leaky singleton. DeleteLeakySingleton(); // The static singleton can't be acquired post-atexit. EXPECT_EQ(nullptr, GetStaticSingleton()); { ShadowingAtExitManager sem; // Verifiy that the variables were reset. { singleton_int = SingletonInt(); EXPECT_EQ(*singleton_int, 0); } { singleton_int_5 = SingletonInt5(); EXPECT_EQ(*singleton_int_5, 5); } { // Resurrect the static singleton, and assert that it // still points to the same (static) memory. CallbackSingletonWithStaticTrait::Trait::ResurrectForTesting(); EXPECT_EQ(GetStaticSingleton(), static_singleton); } } // The leaky singleton shouldn't leak since SingletonLeak has not been called. VerifiesCallbacksNotCalled(); } #define EXPECT_ALIGNED(ptr, align) \ EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1)) TEST_F(SingletonTest, Alignment) { // Create some static singletons with increasing sizes and alignment // requirements. By ordering this way, the linker will need to do some work to // ensure proper alignment of the static data. AlignedTestSingleton<int32_t>* align4 = AlignedTestSingleton<int32_t>::GetInstance(); AlignedTestSingleton<AlignedData<32>>* align32 = AlignedTestSingleton<AlignedData<32>>::GetInstance(); AlignedTestSingleton<AlignedData<128>>* align128 = AlignedTestSingleton<AlignedData<128>>::GetInstance(); AlignedTestSingleton<AlignedData<4096>>* align4096 = AlignedTestSingleton<AlignedData<4096>>::GetInstance(); EXPECT_ALIGNED(align4, 4); EXPECT_ALIGNED(align32, 32); EXPECT_ALIGNED(align128, 128); EXPECT_ALIGNED(align4096, 4096); } } // namespace } // namespace base