0
Files
android_webview
apps
ash
base
allocator
android
apple
containers
debug
files
fuchsia
functional
hash
i18n
ios
json
logging
mac
macros
memory
message_loop
metrics
nix
numerics
posix
power_monitor
process
profiler
sampling_heap_profiler
strings
substring_set_matcher
synchronization
system
task
test
third_party
threading
time
timer
trace_event
tracing
types
version_info
win
BUILD.gn
DEPS
DIR_METADATA
OWNERS
PRESUBMIT.py
README.md
SECURITY_OWNERS
at_exit.cc
at_exit.h
at_exit_unittest.cc
atomic_ref_count.h
atomic_sequence_num.h
atomicops.cc
atomicops.h
atomicops_internals_atomicword_compat.h
atomicops_internals_portable.h
atomicops_unittest.cc
auto_reset.h
auto_reset_unittest.cc
barrier_callback.h
barrier_callback_unittest.cc
barrier_closure.cc
barrier_closure.h
barrier_closure_unittest.cc
base64.cc
base64.h
base64_decode_fuzzer.cc
base64_encode_fuzzer.cc
base64_unittest.cc
base64url.cc
base64url.h
base64url_fuzzer.cc
base64url_unittest.cc
base_export.h
base_paths.cc
base_paths.h
base_paths_android.cc
base_paths_android.h
base_paths_apple.cc
base_paths_apple.h
base_paths_fuchsia.cc
base_paths_ios.h
base_paths_ios.mm
base_paths_mac.h
base_paths_mac.mm
base_paths_posix.cc
base_paths_posix.h
base_paths_win.cc
base_paths_win.h
base_switches.cc
base_switches.h
big_endian.h
big_endian_perftest.cc
bit_cast.h
bit_cast_unittest.cc
bits.h
bits_unittest.cc
build_time.h
build_time_unittest.cc
callback_list.cc
callback_list.h
callback_list_nocompile.nc
callback_list_unittest.cc
cancelable_callback.h
cancelable_callback_unittest.cc
check.cc
check.h
check_deref.h
check_example.cc
check_is_test.cc
check_is_test.h
check_is_test_unittest.cc
check_nocompile.nc
check_op.cc
check_op.h
check_unittest.cc
check_version_internal.h.in
command_line.cc
command_line.h
command_line_fuzzer.cc
command_line_unittest.cc
compiler_specific.h
component_export.h
component_export_unittest.cc
cpu.cc
cpu.h
cpu_unittest.cc
critical_closure.h
critical_closure_internal_ios.mm
dcheck_is_on.h
enterprise_util.cc
enterprise_util.h
enterprise_util_mac.mm
enterprise_util_mac_unittest.mm
enterprise_util_win.cc
environment.cc
environment.h
environment_unittest.cc
export_template.h
feature_list.cc
feature_list.h
feature_list_unittest.cc
feature_visitor.h
features.cc
features.h
file_descriptor_posix.cc
file_descriptor_posix.h
file_descriptor_store.cc
file_descriptor_store.h
file_version_info.h
file_version_info_apple.h
file_version_info_apple.mm
file_version_info_win.cc
file_version_info_win.h
file_version_info_win_unittest.cc
format_macros.h
gmock_unittest.cc
gtest_prod_util.h
immediate_crash.h
immediate_crash_unittest.cc
lazy_instance.h
lazy_instance_helpers.cc
lazy_instance_helpers.h
lazy_instance_unittest.cc
libcpp_hardening_test.cc
linux_util.cc
linux_util.h
linux_util_unittest.cc
location.cc
location.h
location_unittest.cc
logging.cc
logging.h
logging_chromeos.cc
logging_nocompile.nc
logging_unittest.cc
logging_win.cc
logging_win.h
moving_window.h
moving_window_unittest.cc
native_library.cc
native_library.h
native_library_apple.mm
native_library_fuchsia.cc
native_library_posix.cc
native_library_unittest.cc
native_library_win.cc
no_destructor.h
no_destructor_nocompile.nc
no_destructor_unittest.cc
not_fatal_until.h
notimplemented.h
notreached.h
observer_list.h
observer_list_internal.cc
observer_list_internal.h
observer_list_nocompile.nc
observer_list_perftest.cc
observer_list_threadsafe.cc
observer_list_threadsafe.h
observer_list_threadsafe_unittest.cc
observer_list_types.cc
observer_list_types.h
observer_list_unittest.cc
one_shot_event.cc
one_shot_event.h
one_shot_event_unittest.cc
os_compat_android.cc
os_compat_android.h
os_compat_nacl.cc
os_compat_nacl.h
parameter_pack.h
parameter_pack_unittest.cc
path_service.cc
path_service.h
path_service_unittest.cc
pending_task.cc
pending_task.h
pickle.cc
pickle.h
pickle_fuzzer.cc
pickle_unittest.cc
rand_util.cc
rand_util.h
rand_util_fuchsia.cc
rand_util_nacl.cc
rand_util_perftest.cc
rand_util_posix.cc
rand_util_unittest.cc
rand_util_win.cc
run_loop.cc
run_loop.h
run_loop_nocompile.nc
run_loop_unittest.cc
safe_numerics_nocompile.nc
safe_numerics_unittest.cc
scoped_add_feature_flags.cc
scoped_add_feature_flags.h
scoped_add_feature_flags_unittest.cc
scoped_clear_last_error.h
scoped_clear_last_error_unittest.cc
scoped_clear_last_error_win.cc
scoped_environment_variable_override.cc
scoped_environment_variable_override.h
scoped_generic.h
scoped_generic_unittest.cc
scoped_multi_source_observation.h
scoped_multi_source_observation_unittest.cc
scoped_native_library.cc
scoped_native_library.h
scoped_native_library_unittest.cc
scoped_observation.h
scoped_observation_traits.h
scoped_observation_unittest.cc
security_unittest.cc
sequence_checker.cc
sequence_checker.h
sequence_checker_impl.cc
sequence_checker_impl.h
sequence_checker_nocompile.nc
sequence_checker_unittest.cc
sequence_token.cc
sequence_token.h
sequence_token_unittest.cc
stack_canary_linux.cc
stack_canary_linux.h
stack_canary_linux_unittest.cc
state_transitions.h
state_transitions_unittest.cc
std_clamp_unittest.cc
stl_util.h
stl_util_unittest.cc
supports_user_data.cc
supports_user_data.h
supports_user_data_unittest.cc
sync_socket.cc
sync_socket.h
sync_socket_nacl.cc
sync_socket_posix.cc
sync_socket_unittest.cc
sync_socket_win.cc
sys_byteorder.h
sys_byteorder_unittest.cc
syslog_logging.cc
syslog_logging.h
thread_annotations.h
thread_annotations_nocompile.nc
thread_annotations_unittest.cc
token.cc
token.h
token_unittest.cc
tools_sanity_unittest.cc
traits_bag.h
traits_bag_nocompile.nc
traits_bag_unittest.cc
tuple.h
tuple_unittest.cc
unguessable_token.cc
unguessable_token.h
unguessable_token_unittest.cc
unsafe_buffers_nocompile.nc
unsafe_buffers_unittest.cc
uuid.cc
uuid.h
uuid_unittest.cc
value_iterators.cc
value_iterators.h
value_iterators_unittest.cc
values.cc
values.h
values_nocompile.nc
values_unittest.cc
version.cc
version.h
version_unittest.cc
vlog.cc
vlog.h
vlog_unittest.cc
write_build_date_header.py
build
build_overrides
buildtools
cc
chrome
chromecast
chromeos
clank
codelabs
components
content
crypto
dbus
device
docs
extensions
fuchsia_web
gin
google_apis
gpu
headless
infra
internal
ios
ios_internal
ipc
media
mojo
native_client
native_client_sdk
net
pdf
ppapi
printing
remoting
rlz
sandbox
services
signing_keys
skia
sql
storage
styleguide
testing
third_party
tools
ui
url
v8
webkit
.clang-format
.clang-tidy
.clangd
.git-blame-ignore-revs
.gitallowed
.gitattributes
.gitignore
.gitmodules
.gn
.mailmap
.rustfmt.toml
.vpython3
.yapfignore
ATL_OWNERS
AUTHORS
BUILD.gn
CODE_OF_CONDUCT.md
CPPLINT.cfg
CRYPTO_OWNERS
DEPS
DIR_METADATA
LICENSE
LICENSE.chromium_os
OWNERS
PRESUBMIT.py
PRESUBMIT_test.py
PRESUBMIT_test_mocks.py
README.md
WATCHLISTS
codereview.settings
src/base/lazy_instance_unittest.cc
Peter Kasting 811504a7aa Apply clang-tidy autofixes to base/.
* Ran clang-tidy with -fix.
* Ran git cl format.
* Reverted any fixes which caused local problems (mostly because of
  making constructors explicit and breaking unittests that depended
  on them being implicit).

Other minor hand-edits applied during rebasing.

This does not make base/ fully clang-tidy clean, but it should greatly
reduce the number of existing errors.

Bug: none
Change-Id: I93a046f2838e6494812fa038b776b22b8dea93a7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6119194
Reviewed-by: Lei Zhang <thestig@chromium.org>
Owners-Override: Lei Zhang <thestig@chromium.org>
Commit-Queue: Peter Kasting <pkasting@chromium.org>
Auto-Submit: Peter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1403975}
2025-01-08 19:18:50 -08:00

323 lines
10 KiB
C++

// 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_;
class ConstructAndDestructLogger {
public:
ConstructAndDestructLogger() { constructed_seq_.GetNext(); }
ConstructAndDestructLogger(const ConstructAndDestructLogger&) = delete;
ConstructAndDestructLogger& operator=(const ConstructAndDestructLogger&) =
delete;
~ConstructAndDestructLogger() { destructed_seq_.GetNext(); }
};
class SlowConstructor {
public:
SlowConstructor() {
// Sleep for 1 second to try to cause a race.
base::PlatformThread::Sleep(base::Seconds(1));
++constructed;
some_int_ = 12;
}
SlowConstructor(const SlowConstructor&) = delete;
SlowConstructor& operator=(const SlowConstructor&) = delete;
int some_int() const { return some_int_; }
static int constructed;
private:
int some_int_ = 0;
};
// static
int SlowConstructor::constructed = 0;
class SlowDelegate : public base::DelegateSimpleThread::Delegate {
public:
explicit SlowDelegate(
base::LazyInstance<SlowConstructor>::DestructorAtExit* lazy)
: lazy_(lazy) {}
SlowDelegate(const SlowDelegate&) = delete;
SlowDelegate& operator=(const SlowDelegate&) = delete;
void Run() 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;
SlowDelegate delegate(&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.
class DeleteLogger {
public:
DeleteLogger() : deleted_(nullptr) {}
~DeleteLogger() { *deleted_ = true; }
void SetDeletedPtr(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>
class AlignedData {
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.
static LazyInstance<AlignedData<4>>::DestructorAtExit align4 =
LAZY_INSTANCE_INITIALIZER;
static LazyInstance<AlignedData<32>>::DestructorAtExit align32 =
LAZY_INSTANCE_INITIALIZER;
static LazyInstance<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.
class BlockingConstructor {
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(const BlockingConstructor&) = delete;
BlockingConstructor& operator=(const BlockingConstructor&) = 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.
static bool WasConstructorCalled() {
return constructor_called_.load(std::memory_order_relaxed);
}
// Instructs BlockingConstructor() that it may now unblock its construction.
static void CompleteConstructionNow() {
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.
class BlockingConstructorThread : 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(const BlockingConstructorThread&) = delete;
BlockingConstructorThread& operator=(const BlockingConstructorThread&) =
delete;
void Run() 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.
const int 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));
}