Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /refs/heads/main /. /base /lazy_instance_unittest.cc
blob: f20c426beec7080b35bf5ea7ae8f7a458a21d4e9 [file] [log] [blame] [edit]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include"base/lazy_instance.h"
#include<stddef.h>
#include<atomic>
#include<memory>
#include<utility>
#include<vector>
#include"base/at_exit.h"
#include"base/atomic_sequence_num.h"
#include"base/barrier_closure.h"
#include"base/functional/bind.h"
#include"base/functional/callback.h"
#include"base/memory/aligned_memory.h"
#include"base/memory/raw_ptr.h"
#include"base/system/sys_info.h"
#include"base/threading/platform_thread.h"
#include"base/threading/simple_thread.h"
#include"base/time/time.h"
#include"build/build_config.h"
#include"testing/gtest/include/gtest/gtest.h"
namespace{
base::AtomicSequenceNumber constructed_seq_;
base::AtomicSequenceNumber destructed_seq_;
classConstructAndDestructLogger{
public:
ConstructAndDestructLogger(){ constructed_seq_.GetNext();}
ConstructAndDestructLogger(constConstructAndDestructLogger&)=delete;
ConstructAndDestructLogger&operator=(constConstructAndDestructLogger&)=
delete;
~ConstructAndDestructLogger(){ destructed_seq_.GetNext();}
};
classSlowConstructor{
public:
SlowConstructor(){
// Sleep for 1 second to try to cause a race.
base::PlatformThread::Sleep(base::Seconds(1));
++constructed;
some_int_=12;
}
SlowConstructor(constSlowConstructor&)=delete;
SlowConstructor&operator=(constSlowConstructor&)=delete;
int some_int()const{return some_int_;}
staticint constructed;
private:
int some_int_=0;
};
// static
intSlowConstructor::constructed=0;
classSlowDelegate:public base::DelegateSimpleThread::Delegate{
public:
explicitSlowDelegate(
base::LazyInstance<SlowConstructor>::DestructorAtExit* lazy)
: lazy_(lazy){}
SlowDelegate(constSlowDelegate&)=delete;
SlowDelegate&operator=(constSlowDelegate&)=delete;
voidRun() override{
EXPECT_EQ(12, lazy_->Get().some_int());
EXPECT_EQ(12, lazy_->Pointer()->some_int());
}
private:
raw_ptr<base::LazyInstance<SlowConstructor>::DestructorAtExit> lazy_;
};
}// namespace
base::LazyInstance<ConstructAndDestructLogger>::DestructorAtExit lazy_logger=
LAZY_INSTANCE_INITIALIZER;
TEST(LazyInstanceTest,Basic){
{
base::ShadowingAtExitManager shadow;
EXPECT_FALSE(lazy_logger.IsCreated());
EXPECT_EQ(0, constructed_seq_.GetNext());
EXPECT_EQ(0, destructed_seq_.GetNext());
lazy_logger.Get();
EXPECT_TRUE(lazy_logger.IsCreated());
EXPECT_EQ(2, constructed_seq_.GetNext());
EXPECT_EQ(1, destructed_seq_.GetNext());
lazy_logger.Pointer();
EXPECT_TRUE(lazy_logger.IsCreated());
EXPECT_EQ(3, constructed_seq_.GetNext());
EXPECT_EQ(2, destructed_seq_.GetNext());
}
EXPECT_FALSE(lazy_logger.IsCreated());
EXPECT_EQ(4, constructed_seq_.GetNext());
EXPECT_EQ(4, destructed_seq_.GetNext());
}
base::LazyInstance<SlowConstructor>::DestructorAtExit lazy_slow=
LAZY_INSTANCE_INITIALIZER;
TEST(LazyInstanceTest,ConstructorThreadSafety){
{
base::ShadowingAtExitManager shadow;
SlowDelegatedelegate(&lazy_slow);
EXPECT_EQ(0,SlowConstructor::constructed);
base::DelegateSimpleThreadPool pool("lazy_instance_cons",5);
pool.AddWork(&delegate,20);
EXPECT_EQ(0,SlowConstructor::constructed);
pool.Start();
pool.JoinAll();
EXPECT_EQ(1,SlowConstructor::constructed);
}
}
namespace{
// DeleteLogger is an object which sets a flag when it's destroyed.
// It accepts a bool* and sets the bool to true when the dtor runs.
classDeleteLogger{
public:
DeleteLogger(): deleted_(nullptr){}
~DeleteLogger(){*deleted_=true;}
voidSetDeletedPtr(bool* deleted){ deleted_= deleted;}
private:
raw_ptr<bool> deleted_;
};
}// anonymous namespace
TEST(LazyInstanceTest,LeakyLazyInstance){
// Check that using a plain LazyInstance causes the dtor to run
// when the AtExitManager finishes.
bool deleted1=false;
{
base::ShadowingAtExitManager shadow;
static base::LazyInstance<DeleteLogger>::DestructorAtExit test=
LAZY_INSTANCE_INITIALIZER;
test.Get().SetDeletedPtr(&deleted1);
}
EXPECT_TRUE(deleted1);
// Check that using a *leaky* LazyInstance makes the dtor not run
// when the AtExitManager finishes.
bool deleted2=false;
{
base::ShadowingAtExitManager shadow;
static base::LazyInstance<DeleteLogger>::Leaky test=
LAZY_INSTANCE_INITIALIZER;
test.Get().SetDeletedPtr(&deleted2);
}
EXPECT_FALSE(deleted2);
}
namespace{
template<size_t alignment>
classAlignedData{
public:
AlignedData()=default;
~AlignedData()=default;
alignas(alignment)char data_[alignment];
};
}// namespace
TEST(LazyInstanceTest,Alignment){
using base::LazyInstance;
// Create some static instances 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.
staticLazyInstance<AlignedData<4>>::DestructorAtExit align4=
LAZY_INSTANCE_INITIALIZER;
staticLazyInstance<AlignedData<32>>::DestructorAtExit align32=
LAZY_INSTANCE_INITIALIZER;
staticLazyInstance<AlignedData<4096>>::DestructorAtExit align4096=
LAZY_INSTANCE_INITIALIZER;
EXPECT_TRUE(base::IsAligned(align4.Pointer(),4));
EXPECT_TRUE(base::IsAligned(align32.Pointer(),32));
EXPECT_TRUE(base::IsAligned(align4096.Pointer(),4096));
}
namespace{
// A class whose constructor busy-loops until it is told to complete
// construction.
classBlockingConstructor{
public:
BlockingConstructor(){
EXPECT_FALSE(WasConstructorCalled());
constructor_called_.store(true, std::memory_order_relaxed);
EXPECT_TRUE(WasConstructorCalled());
while(!complete_construction_.load(std::memory_order_relaxed)){
base::PlatformThread::YieldCurrentThread();
}
done_construction_=true;
}
BlockingConstructor(constBlockingConstructor&)=delete;
BlockingConstructor&operator=(constBlockingConstructor&)=delete;
~BlockingConstructor(){
// Restore static state for the next test.
constructor_called_.store(false, std::memory_order_relaxed);
complete_construction_.store(false, std::memory_order_relaxed);
}
// Returns true if BlockingConstructor() was entered.
staticboolWasConstructorCalled(){
return constructor_called_.load(std::memory_order_relaxed);
}
// Instructs BlockingConstructor() that it may now unblock its construction.
staticvoidCompleteConstructionNow(){
complete_construction_.store(true, std::memory_order_relaxed);
}
bool done_construction()const{return done_construction_;}
private:
// Use Atomic32 instead of AtomicFlag for them to be trivially initialized.
static std::atomic<bool> constructor_called_;
static std::atomic<bool> complete_construction_;
bool done_construction_=false;
};
// A SimpleThread running at |thread_type| which invokes |before_get| (optional)
// and then invokes Get() on the LazyInstance it's assigned.
classBlockingConstructorThread:public base::SimpleThread{
public:
BlockingConstructorThread(
base::ThreadType thread_type,
base::LazyInstance<BlockingConstructor>::DestructorAtExit* lazy,
base::OnceClosure before_get)
:SimpleThread("BlockingConstructorThread",Options(thread_type)),
lazy_(lazy),
before_get_(std::move(before_get)){}
BlockingConstructorThread(constBlockingConstructorThread&)=delete;
BlockingConstructorThread&operator=(constBlockingConstructorThread&)=
delete;
voidRun() override{
if(before_get_){
std::move(before_get_).Run();
}
EXPECT_TRUE(lazy_->Get().done_construction());
}
private:
raw_ptr<base::LazyInstance<BlockingConstructor>::DestructorAtExit> lazy_;
base::OnceClosure before_get_;
};
// static
std::atomic<bool>BlockingConstructor::constructor_called_=false;
// static
std::atomic<bool>BlockingConstructor::complete_construction_=false;
base::LazyInstance<BlockingConstructor>::DestructorAtExit lazy_blocking=
LAZY_INSTANCE_INITIALIZER;
}// namespace
// Tests that if the thread assigned to construct the LazyInstance runs at
// background priority : the foreground threads will yield to it enough for it
// to eventually complete construction.
// This is a regression test for https://crbug.com/797129.
TEST(LazyInstanceTest,PriorityInversionAtInitializationResolves){
base::TimeTicks test_begin= base::TimeTicks::Now();
// Construct BlockingConstructor from a background thread.
BlockingConstructorThread background_getter(
base::ThreadType::kBackground,&lazy_blocking, base::OnceClosure());
background_getter.Start();
while(!BlockingConstructor::WasConstructorCalled()){
base::PlatformThread::Sleep(base::Milliseconds(1));
}
// Spin 4 foreground thread per core contending to get the already under
// construction LazyInstance. When they are all running and poking at it :
// allow the background thread to complete its work.
constint kNumForegroundThreads=4* base::SysInfo::NumberOfProcessors();
std::vector<std::unique_ptr<base::SimpleThread>> foreground_threads;
base::RepeatingClosure foreground_thread_ready_callback=
base::BarrierClosure(
kNumForegroundThreads,
base::BindOnce(&BlockingConstructor::CompleteConstructionNow));
for(int i=0; i< kNumForegroundThreads;++i){
foreground_threads.push_back(std::make_unique<BlockingConstructorThread>(
base::ThreadType::kDefault,&lazy_blocking,
foreground_thread_ready_callback));
foreground_threads.back()->Start();
}
// This test will hang if the foreground threads become stuck in
// LazyInstance::Get() per the background thread never being scheduled to
// complete construction.
for(auto& foreground_thread: foreground_threads){
foreground_thread->Join();
}
background_getter.Join();
// Fail if this test takes more than 5 seconds (it takes 5-10 seconds on a
// Z840 without r527445 but is expected to be fast (~30ms) with the fix).
EXPECT_LT(base::TimeTicks::Now()- test_begin, base::Seconds(5));
}

[8]ページ先頭

©2009-2025 Movatter.jp