Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /refs/heads/main /. /base /sequence_checker_unittest.cc
blob: b7ac5235d640d597b5447c7dacba6a20bc69f573 [file] [log] [blame] [edit]
// Copyright 2013 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/sequence_checker.h"
#include<stddef.h>
#include<memory>
#include<string>
#include<utility>
#include"base/functional/bind.h"
#include"base/functional/callback_helpers.h"
#include"base/sequence_checker_impl.h"
#include"base/sequence_token.h"
#include"base/synchronization/lock.h"
#include"base/synchronization/lock_subtle.h"
#include"base/task/single_thread_task_runner.h"
#include"base/task/thread_pool.h"
#include"base/test/bind.h"
#include"base/test/gtest_util.h"
#include"base/test/task_environment.h"
#include"base/threading/simple_thread.h"
#include"base/threading/thread_local.h"
#include"testing/gtest/include/gtest/gtest.h"
namespace base::internal{
namespace{
// Runs a callback on another thread.
classRunCallbackThread:publicSimpleThread{
public:
explicitRunCallbackThread(OnceClosure callback)
:SimpleThread("RunCallbackThread"), callback_(std::move(callback)){
Start();
Join();
}
RunCallbackThread(constRunCallbackThread&)=delete;
RunCallbackThread&operator=(constRunCallbackThread&)=delete;
private:
// SimpleThread:
voidRun() override{ std::move(callback_).Run();}
OnceClosure callback_;
};
voidExpectCalledOnValidSequence(SequenceCheckerImpl* sequence_checker){
ASSERT_TRUE(sequence_checker);
// This should bind |sequence_checker| to the current sequence if it wasn't
// already bound to a sequence.
EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
// Since |sequence_checker| is now bound to the current sequence, another call
// to CalledOnValidSequence() should return true.
EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
}
voidExpectNotCalledOnValidSequence(SequenceCheckerImpl* sequence_checker){
ASSERT_TRUE(sequence_checker);
EXPECT_FALSE(sequence_checker->CalledOnValidSequence());
}
}// namespace
TEST(SequenceCheckerTest,NoTaskScope){
SequenceCheckerImpl sequence_checker;
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
}
TEST(SequenceCheckerTest,TaskScope){
TaskScope task_scope(SequenceToken::Create(),
/* is_thread_bound=*/false);
SequenceCheckerImpl sequence_checker;
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
}
TEST(SequenceCheckerTest,TaskScopeThreadBound){
TaskScope task_scope(SequenceToken::Create(),
/* is_thread_bound=*/true);
SequenceCheckerImpl sequence_checker;
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
}
TEST(SequenceCheckerTest,DifferentThreadNoTaskScope){
SequenceCheckerImpl sequence_checker;
RunCallbackThread thread(
BindOnce(&ExpectNotCalledOnValidSequence,Unretained(&sequence_checker)));
}
TEST(SequenceCheckerTest,DifferentThreadDifferentSequenceToken){
SequenceCheckerImpl sequence_checker;
RunCallbackThread thread(BindLambdaForTesting([&]{
TaskScope task_scope(SequenceToken::Create(),
/* is_thread_bound=*/false);
ExpectNotCalledOnValidSequence(&sequence_checker);
}));
}
TEST(SequenceCheckerTest,DifferentThreadDifferentSequenceTokenThreadBound){
SequenceCheckerImpl sequence_checker;
RunCallbackThread thread(BindLambdaForTesting([&]{
TaskScope task_scope(SequenceToken::Create(),
/* is_thread_bound=*/true);
ExpectNotCalledOnValidSequence(&sequence_checker);
}));
}
TEST(SequenceCheckerTest,DifferentThreadSameSequenceToken){
constSequenceToken token=SequenceToken::Create();
TaskScope task_scope(token,/* is_thread_bound=*/false);
SequenceCheckerImpl sequence_checker;
RunCallbackThread thread(BindLambdaForTesting([&]{
TaskScope task_scope(token,/* is_thread_bound=*/false);
ExpectCalledOnValidSequence(&sequence_checker);
}));
}
TEST(SequenceCheckerTest,DifferentThreadSameSequenceTokenThreadBound){
// Note: A callback running synchronously in `RunOrPostTask()` may have a
// non-thread-bound `TaskScope` associated with the same `SequenceToken` as
// another thread-bound `TaskScope`. This test recreates this case.
constSequenceToken token=SequenceToken::Create();
TaskScope task_scope(token,/* is_thread_bound=*/true);
SequenceCheckerImpl sequence_checker;
RunCallbackThread thread(BindLambdaForTesting([&]{
TaskScope task_scope(token,/* is_thread_bound=*/false);
ExpectCalledOnValidSequence(&sequence_checker);
}));
}
TEST(SequenceCheckerTest,SameThreadDifferentSequenceToken){
std::unique_ptr<SequenceCheckerImpl> sequence_checker;
{
TaskScope task_scope(SequenceToken::Create(),
/* is_thread_bound=*/false);
sequence_checker= std::make_unique<SequenceCheckerImpl>();
}
{
// Different SequenceToken.
TaskScope task_scope(SequenceToken::Create(),
/* is_thread_bound=*/false);
EXPECT_FALSE(sequence_checker->CalledOnValidSequence());
}
// No explicit SequenceToken.
EXPECT_FALSE(sequence_checker->CalledOnValidSequence());
}
TEST(SequenceCheckerTest,DetachFromSequence){
std::unique_ptr<SequenceCheckerImpl> sequence_checker;
{
TaskScope task_scope(SequenceToken::Create(),
/* is_thread_bound=*/false);
sequence_checker= std::make_unique<SequenceCheckerImpl>();
}
sequence_checker->DetachFromSequence();
{
// Verify that CalledOnValidSequence() returns true when called with
// a different sequence token after a call to DetachFromSequence().
TaskScope task_scope(SequenceToken::Create(),
/* is_thread_bound=*/false);
EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
}
}
TEST(SequenceCheckerTest,DetachFromSequenceNoSequenceToken){
SequenceCheckerImpl sequence_checker;
sequence_checker.DetachFromSequence();
// Verify that CalledOnValidSequence() returns true when called on a
// different thread after a call to DetachFromSequence().
RunCallbackThread thread(
BindOnce(&ExpectCalledOnValidSequence,Unretained(&sequence_checker)));
EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
}
TEST(SequenceCheckerTest,Move){
SequenceCheckerImpl initial;
EXPECT_TRUE(initial.CalledOnValidSequence());
SequenceCheckerImpl move_constructed(std::move(initial));
EXPECT_TRUE(move_constructed.CalledOnValidSequence());
SequenceCheckerImpl move_assigned;
move_assigned= std::move(move_constructed);
// The two SequenceCheckerImpls moved from should be able to rebind to another
// sequence.
RunCallbackThread thread1(
BindOnce(&ExpectCalledOnValidSequence,Unretained(&initial)));
RunCallbackThread thread2(
BindOnce(&ExpectCalledOnValidSequence,Unretained(&move_constructed)));
// But the latest one shouldn't be able to run on another sequence.
RunCallbackThread thread(
BindOnce(&ExpectNotCalledOnValidSequence,Unretained(&move_assigned)));
EXPECT_TRUE(move_assigned.CalledOnValidSequence());
}
TEST(SequenceCheckerTest,MoveAssignIntoDetached){
SequenceCheckerImpl initial;
SequenceCheckerImpl move_assigned;
move_assigned.DetachFromSequence();
move_assigned= std::move(initial);
// |initial| is detached after move.
RunCallbackThread thread1(
BindOnce(&ExpectCalledOnValidSequence,Unretained(&initial)));
// |move_assigned| should be associated with the main thread.
RunCallbackThread thread2(
BindOnce(&ExpectNotCalledOnValidSequence,Unretained(&move_assigned)));
EXPECT_TRUE(move_assigned.CalledOnValidSequence());
}
TEST(SequenceCheckerTest,MoveFromDetachedRebinds){
SequenceCheckerImpl initial;
initial.DetachFromSequence();
SequenceCheckerImpl moved_into(std::move(initial));
// |initial| is still detached after move.
RunCallbackThread thread1(
BindOnce(&ExpectCalledOnValidSequence,Unretained(&initial)));
// |moved_into| is bound to the current sequence as part of the move.
RunCallbackThread thread2(
BindOnce(&ExpectNotCalledOnValidSequence,Unretained(&moved_into)));
EXPECT_TRUE(moved_into.CalledOnValidSequence());
}
TEST(SequenceCheckerTest,MoveOffSequenceBanned){
GTEST_FLAG_SET(death_test_style,"threadsafe");
SequenceCheckerImpl other_sequence;
other_sequence.DetachFromSequence();
RunCallbackThread thread(
BindOnce(&ExpectCalledOnValidSequence,Unretained(&other_sequence)));
EXPECT_CHECK_DEATH(
SequenceCheckerImpl main_sequence(std::move(other_sequence)));
}
TEST(SequenceCheckerMacroTest,Macros){
auto scope= std::make_unique<TaskScope>(SequenceToken::Create(),
/* is_thread_bound=*/false);
SEQUENCE_CHECKER(my_sequence_checker);
{
// Don't expect a DCHECK death when a SequenceChecker is used on the right
// sequence.
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker);
}
scope.reset();
#if DCHECK_IS_ON()
// Expect DCHECK death when used on a different sequence.
EXPECT_DCHECK_DEATH(
{ DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker);});
#else
// Happily no-ops on non-dcheck builds.
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker);
#endif
DETACH_FROM_SEQUENCE(my_sequence_checker);
// Don't expect a DCHECK death when a SequenceChecker is used for the first
// time after having been detached.
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker);
}
// Owns a SequenceCheckerImpl, and asserts that CalledOnValidSequence() is valid
// in ~SequenceCheckerOwner.
classSequenceCheckerOwner{
public:
explicitSequenceCheckerOwner(SequenceCheckerImpl* other_checker)
: other_checker_(other_checker){}
SequenceCheckerOwner(constSequenceCheckerOwner&)=delete;
SequenceCheckerOwner&operator=(constSequenceCheckerOwner&)=delete;
~SequenceCheckerOwner(){
// Check passes on TLS destruction.
EXPECT_TRUE(checker_.CalledOnValidSequence());
// Check also passes on TLS destruction after move assignment.
*other_checker_= std::move(checker_);
EXPECT_TRUE(other_checker_->CalledOnValidSequence());
}
private:
SequenceCheckerImpl checker_;
raw_ptr<SequenceCheckerImpl> other_checker_;
};
// Verifies SequenceCheckerImpl::CalledOnValidSequence() returns true if called
// during thread destruction.
TEST(SequenceCheckerTest,FromThreadDestruction){
SequenceChecker::EnableStackLogging();
SequenceCheckerImpl other_checker;
ThreadLocalOwnedPointer<SequenceCheckerOwner> thread_local_owner;
{
test::TaskEnvironment task_environment;
auto task_runner=ThreadPool::CreateSequencedTaskRunner({});
task_runner->PostTask(
FROM_HERE,BindLambdaForTesting([&]{
thread_local_owner.Set(
std::make_unique<SequenceCheckerOwner>(&other_checker));
}));
task_runner=nullptr;
task_environment.RunUntilIdle();
}
}
// Verifies sequence checking while holding the same locks from different
// sequences.
//
// Note: This is only supported in DCHECK builds.
#if DCHECK_IS_ON()
TEST(SequenceCheckerTest,LockBasic){
test::TaskEnvironment task_environment;
WaitableEvent thread_pool_done;
Lock lock;
// Create sequence checker while holding lock.
ReleasableAutoLock releasable_auto_lock(&lock,
subtle::LockTracking::kEnabled);
SequenceCheckerImpl sequence_checker;
releasable_auto_lock.Release();
ThreadPool::PostTask(BindLambdaForTesting([&]{
// Check sequencing while holding the lock.
{
AutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
}
thread_pool_done.Signal();
}));
thread_pool_done.Wait();
// Check sequencing from the creation sequence, without holding the lock.
// Sequencing is *not* valid because sequencing now depends on the lock.
EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
// Check sequencing from the creation sequence while holding the lock.
{
AutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
}
}
TEST(SequenceCheckerTest,ManyLocks){
test::TaskEnvironment task_environment;
WaitableEvent thread_pool_done;
Lock lock_a;
Lock lock_b;
Lock lock_c;
ReleasableAutoLock releasable_auto_lock_a(&lock_a,
subtle::LockTracking::kEnabled);
ReleasableAutoLock releasable_auto_lock_b(&lock_b,
subtle::LockTracking::kEnabled);
ReleasableAutoLock releasable_auto_lock_c(&lock_c,
subtle::LockTracking::kEnabled);
SequenceCheckerImpl sequence_checker;
releasable_auto_lock_c.Release();
releasable_auto_lock_b.Release();
releasable_auto_lock_a.Release();
ThreadPool::PostTask(BindLambdaForTesting([&]{
{
AutoLock auto_lock_a(lock_a, subtle::LockTracking::kEnabled);
AutoLock auto_lock_b(lock_b, subtle::LockTracking::kEnabled);
AutoLock auto_lock_c(lock_c, subtle::LockTracking::kEnabled);
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
}
{
AutoLock auto_lock_a(lock_a, subtle::LockTracking::kEnabled);
AutoLock auto_lock_b(lock_b, subtle::LockTracking::kEnabled);
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
}
{
AutoLock auto_lock_c(lock_c, subtle::LockTracking::kEnabled);
EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
}
{
AutoLock auto_lock_b(lock_b, subtle::LockTracking::kEnabled);
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
}
{
AutoLock auto_lock_b(lock_a, subtle::LockTracking::kEnabled);
EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
}
thread_pool_done.Signal();
}));
thread_pool_done.Wait();
EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
}
TEST(SequenceCheckerTest,LockAndSequence){
test::TaskEnvironment task_environment;
WaitableEvent thread_pool_done;
Lock lock;
// Create sequence checker while holding lock.
ReleasableAutoLock releasable_auto_lock(&lock,
subtle::LockTracking::kEnabled);
SequenceCheckerImpl sequence_checker;
releasable_auto_lock.Release();
// Check sequencing without holding the lock.
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
ThreadPool::PostTask(BindLambdaForTesting([&]{
// Check sequencing while holding the lock. This is not valid because
// `CalledOnValidSequence()` previously returned true while the lock wasn't
// held.
{
AutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
}
thread_pool_done.Signal();
}));
thread_pool_done.Wait();
}
TEST(SequenceCheckerTest,LockDetachFromSequence){
test::TaskEnvironment task_environment;
WaitableEvent thread_pool_done;
Lock lock;
// Create sequence checker and detach while holding lock.
ReleasableAutoLock releasable_auto_lock(&lock,
subtle::LockTracking::kEnabled);
SequenceCheckerImpl sequence_checker;
sequence_checker.DetachFromSequence();
releasable_auto_lock.Release();
// Re-bind without holding the lock.
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
ThreadPool::PostTask(BindLambdaForTesting([&]{
// Check sequencing while holding the lock. This is not valid because the
// sequence checker was detached and re-bound without the lock.
{
AutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
}
thread_pool_done.Signal();
}));
thread_pool_done.Wait();
}
TEST(SequenceCheckerTest,LockMoveConstruction){
test::TaskEnvironment task_environment;
WaitableEvent thread_pool_done;
Lock lock;
// Create sequence checker and move-construct while holding a lock.
ReleasableAutoLock releasable_auto_lock(&lock,
subtle::LockTracking::kEnabled);
SequenceCheckerImpl sequence_checker;
SequenceCheckerImpl other_sequence_checker(std::move(sequence_checker));
releasable_auto_lock.Release();
ThreadPool::PostTask(BindLambdaForTesting([&]{
// Check sequencing while holding the lock.
{
AutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
EXPECT_TRUE(other_sequence_checker.CalledOnValidSequence());
}
thread_pool_done.Signal();
}));
thread_pool_done.Wait();
}
TEST(SequenceCheckerTest,LockMoveAssignment){
test::TaskEnvironment task_environment;
WaitableEvent thread_pool_done;
Lock lock;
SequenceCheckerImpl other_sequence_checker;
// Create sequence checker and move-assign it to `other_sequence_checker`
// while holding a lock.
ReleasableAutoLock releasable_auto_lock(&lock,
subtle::LockTracking::kEnabled);
SequenceCheckerImpl sequence_checker;
other_sequence_checker= std::move(sequence_checker);
releasable_auto_lock.Release();
ThreadPool::PostTask(BindLambdaForTesting([&]{
// Check sequencing while holding the lock.
{
AutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
EXPECT_TRUE(other_sequence_checker.CalledOnValidSequence());
}
thread_pool_done.Signal();
}));
thread_pool_done.Wait();
}
#endif// DCHECK_IS_ON()
}// namespace base::internal

[8]ページ先頭

©2009-2025 Movatter.jp