#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 {
class RunCallbackThread : public SimpleThread {
public:
explicit RunCallbackThread(OnceClosure callback)
: SimpleThread("RunCallbackThread"), callback_(std::move(callback)) {
Start();
Join();
}
RunCallbackThread(const RunCallbackThread&) = delete;
RunCallbackThread& operator=(const RunCallbackThread&) = delete;
private:
void Run() override { std::move(callback_).Run(); }
OnceClosure callback_;
};
void ExpectCalledOnValidSequence(SequenceCheckerImpl* sequence_checker) {
ASSERT_TRUE(sequence_checker);
EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
}
void ExpectNotCalledOnValidSequence(SequenceCheckerImpl* sequence_checker) {
ASSERT_TRUE(sequence_checker);
EXPECT_FALSE(sequence_checker->CalledOnValidSequence());
}
}
TEST(SequenceCheckerTest, NoTaskScope) {
SequenceCheckerImpl sequence_checker;
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
}
TEST(SequenceCheckerTest, TaskScope) {
TaskScope task_scope(SequenceToken::Create(),
false);
SequenceCheckerImpl sequence_checker;
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
}
TEST(SequenceCheckerTest, TaskScopeThreadBound) {
TaskScope task_scope(SequenceToken::Create(),
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(),
false);
ExpectNotCalledOnValidSequence(&sequence_checker);
}));
}
TEST(SequenceCheckerTest, DifferentThreadDifferentSequenceTokenThreadBound) {
SequenceCheckerImpl sequence_checker;
RunCallbackThread thread(BindLambdaForTesting([&] {
TaskScope task_scope(SequenceToken::Create(),
true);
ExpectNotCalledOnValidSequence(&sequence_checker);
}));
}
TEST(SequenceCheckerTest, DifferentThreadSameSequenceToken) {
const SequenceToken token = SequenceToken::Create();
TaskScope task_scope(token, false);
SequenceCheckerImpl sequence_checker;
RunCallbackThread thread(BindLambdaForTesting([&] {
TaskScope task_scope(token, false);
ExpectCalledOnValidSequence(&sequence_checker);
}));
}
TEST(SequenceCheckerTest, DifferentThreadSameSequenceTokenThreadBound) {
const SequenceToken token = SequenceToken::Create();
TaskScope task_scope(token, true);
SequenceCheckerImpl sequence_checker;
RunCallbackThread thread(BindLambdaForTesting([&] {
TaskScope task_scope(token, false);
ExpectCalledOnValidSequence(&sequence_checker);
}));
}
TEST(SequenceCheckerTest, SameThreadDifferentSequenceToken) {
std::unique_ptr<SequenceCheckerImpl> sequence_checker;
{
TaskScope task_scope(SequenceToken::Create(),
false);
sequence_checker = std::make_unique<SequenceCheckerImpl>();
}
{
TaskScope task_scope(SequenceToken::Create(),
false);
EXPECT_FALSE(sequence_checker->CalledOnValidSequence());
}
EXPECT_FALSE(sequence_checker->CalledOnValidSequence());
}
TEST(SequenceCheckerTest, DetachFromSequence) {
std::unique_ptr<SequenceCheckerImpl> sequence_checker;
{
TaskScope task_scope(SequenceToken::Create(),
false);
sequence_checker = std::make_unique<SequenceCheckerImpl>();
}
sequence_checker->DetachFromSequence();
{
TaskScope task_scope(SequenceToken::Create(),
false);
EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
}
}
TEST(SequenceCheckerTest, DetachFromSequenceNoSequenceToken) {
SequenceCheckerImpl sequence_checker;
sequence_checker.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);
RunCallbackThread thread1(
BindOnce(&ExpectCalledOnValidSequence, Unretained(&initial)));
RunCallbackThread thread2(
BindOnce(&ExpectCalledOnValidSequence, Unretained(&move_constructed)));
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);
RunCallbackThread thread1(
BindOnce(&ExpectCalledOnValidSequence, Unretained(&initial)));
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));
RunCallbackThread thread1(
BindOnce(&ExpectCalledOnValidSequence, Unretained(&initial)));
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(),
false);
SEQUENCE_CHECKER(my_sequence_checker);
{
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker);
}
scope.reset();
#if DCHECK_IS_ON()
EXPECT_DCHECK_DEATH(
{ DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker); });
#else
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker);
#endif
DETACH_FROM_SEQUENCE(my_sequence_checker);
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker);
}
class SequenceCheckerOwner {
public:
explicit SequenceCheckerOwner(SequenceCheckerImpl* other_checker)
: other_checker_(other_checker) {}
SequenceCheckerOwner(const SequenceCheckerOwner&) = delete;
SequenceCheckerOwner& operator=(const SequenceCheckerOwner&) = delete;
~SequenceCheckerOwner() {
EXPECT_TRUE(checker_.CalledOnValidSequence());
*other_checker_ = std::move(checker_);
EXPECT_TRUE(other_checker_->CalledOnValidSequence());
}
private:
SequenceCheckerImpl checker_;
raw_ptr<SequenceCheckerImpl> other_checker_;
};
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();
}
}
#if DCHECK_IS_ON()
TEST(SequenceCheckerTest, LockBasic) {
test::TaskEnvironment task_environment;
WaitableEvent thread_pool_done;
Lock lock;
ReleasableAutoLock releasable_auto_lock(&lock,
subtle::LockTracking::kEnabled);
SequenceCheckerImpl sequence_checker;
releasable_auto_lock.Release();
ThreadPool::PostTask(BindLambdaForTesting([&] {
{
AutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
}
thread_pool_done.Signal();
}));
thread_pool_done.Wait();
EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
{
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;
ReleasableAutoLock releasable_auto_lock(&lock,
subtle::LockTracking::kEnabled);
SequenceCheckerImpl sequence_checker;
releasable_auto_lock.Release();
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
ThreadPool::PostTask(BindLambdaForTesting([&] {
{
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;
ReleasableAutoLock releasable_auto_lock(&lock,
subtle::LockTracking::kEnabled);
SequenceCheckerImpl sequence_checker;
sequence_checker.DetachFromSequence();
releasable_auto_lock.Release();
EXPECT_TRUE(sequence_checker.CalledOnValidSequence());
ThreadPool::PostTask(BindLambdaForTesting([&] {
{
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;
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([&] {
{
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;
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([&] {
{
AutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
EXPECT_TRUE(other_sequence_checker.CalledOnValidSequence());
}
thread_pool_done.Signal();
}));
thread_pool_done.Wait();
}
#endif
}