blob: 4d3f407bcec8f304275938b589c3f0601391c47d [file] [log] [blame]
// 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 "base/memory/singleton.h"
#include "base/at_exit.h"
#include "starboard/types.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();
#if !defined(STARBOARD)
AlignedTestSingleton<AlignedData<128>>* align128 =
AlignedTestSingleton<AlignedData<128>>::GetInstance();
AlignedTestSingleton<AlignedData<4096>>* align4096 =
AlignedTestSingleton<AlignedData<4096>>::GetInstance();
#endif
EXPECT_ALIGNED(align4, 4);
EXPECT_ALIGNED(align32, 32);
// At least on Raspi, alignas with big alignment numbers does not work and
// that is compliant with C++ standard as the alignment is larger than
// std::max_align_t.
#if !defined(STARBOARD)
EXPECT_ALIGNED(align128, 128);
EXPECT_ALIGNED(align4096, 4096);
#endif
}
} // namespace
} // namespace base