blob: 5fbbc5284a78d9615401d83d006d1cc64aeb076c [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/threading/thread_checker.h"
#include <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/sequence_token.h"
#include "base/test/gtest_util.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/simple_thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
// A thread that runs a callback.
class RunCallbackThread : public SimpleThread {
public:
explicit RunCallbackThread(const Closure& callback)
: SimpleThread("RunCallbackThread"), callback_(callback) {}
private:
// SimpleThread:
void Run() override { callback_.Run(); }
const Closure callback_;
DISALLOW_COPY_AND_ASSIGN(RunCallbackThread);
};
// Runs a callback on a new thread synchronously.
void RunCallbackOnNewThreadSynchronously(const Closure& callback) {
RunCallbackThread run_callback_thread(callback);
run_callback_thread.Start();
run_callback_thread.Join();
}
void ExpectCalledOnValidThread(ThreadCheckerImpl* thread_checker) {
ASSERT_TRUE(thread_checker);
// This should bind |thread_checker| to the current thread if it wasn't
// already bound to a thread.
EXPECT_TRUE(thread_checker->CalledOnValidThread());
// Since |thread_checker| is now bound to the current thread, another call to
// CalledOnValidThread() should return true.
EXPECT_TRUE(thread_checker->CalledOnValidThread());
}
void ExpectNotCalledOnValidThread(ThreadCheckerImpl* thread_checker) {
ASSERT_TRUE(thread_checker);
EXPECT_FALSE(thread_checker->CalledOnValidThread());
}
void ExpectNotCalledOnValidThreadWithSequenceTokenAndThreadTaskRunnerHandle(
ThreadCheckerImpl* thread_checker,
SequenceToken sequence_token) {
ThreadTaskRunnerHandle thread_task_runner_handle(
MakeRefCounted<TestSimpleTaskRunner>());
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(sequence_token);
ExpectNotCalledOnValidThread(thread_checker);
}
} // namespace
TEST(ThreadCheckerTest, AllowedSameThreadNoSequenceToken) {
ThreadCheckerImpl thread_checker;
EXPECT_TRUE(thread_checker.CalledOnValidThread());
}
TEST(ThreadCheckerTest,
AllowedSameThreadAndSequenceDifferentTasksWithThreadTaskRunnerHandle) {
ThreadTaskRunnerHandle thread_task_runner_handle(
MakeRefCounted<TestSimpleTaskRunner>());
std::unique_ptr<ThreadCheckerImpl> thread_checker;
const SequenceToken sequence_token = SequenceToken::Create();
{
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(sequence_token);
thread_checker.reset(new ThreadCheckerImpl);
}
{
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(sequence_token);
EXPECT_TRUE(thread_checker->CalledOnValidThread());
}
}
TEST(ThreadCheckerTest,
AllowedSameThreadSequenceAndTaskNoThreadTaskRunnerHandle) {
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
ThreadCheckerImpl thread_checker;
EXPECT_TRUE(thread_checker.CalledOnValidThread());
}
TEST(ThreadCheckerTest,
DisallowedSameThreadAndSequenceDifferentTasksNoThreadTaskRunnerHandle) {
std::unique_ptr<ThreadCheckerImpl> thread_checker;
{
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
thread_checker.reset(new ThreadCheckerImpl);
}
{
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
EXPECT_FALSE(thread_checker->CalledOnValidThread());
}
}
TEST(ThreadCheckerTest, DisallowedDifferentThreadsNoSequenceToken) {
ThreadCheckerImpl thread_checker;
RunCallbackOnNewThreadSynchronously(
Bind(&ExpectNotCalledOnValidThread, Unretained(&thread_checker)));
}
TEST(ThreadCheckerTest, DisallowedDifferentThreadsSameSequence) {
ThreadTaskRunnerHandle thread_task_runner_handle(
MakeRefCounted<TestSimpleTaskRunner>());
const SequenceToken sequence_token(SequenceToken::Create());
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(sequence_token);
ThreadCheckerImpl thread_checker;
EXPECT_TRUE(thread_checker.CalledOnValidThread());
RunCallbackOnNewThreadSynchronously(Bind(
&ExpectNotCalledOnValidThreadWithSequenceTokenAndThreadTaskRunnerHandle,
Unretained(&thread_checker), sequence_token));
}
TEST(ThreadCheckerTest, DisallowedSameThreadDifferentSequence) {
std::unique_ptr<ThreadCheckerImpl> thread_checker;
ThreadTaskRunnerHandle thread_task_runner_handle(
MakeRefCounted<TestSimpleTaskRunner>());
{
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
thread_checker.reset(new ThreadCheckerImpl);
}
{
// Different SequenceToken.
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
EXPECT_FALSE(thread_checker->CalledOnValidThread());
}
// No SequenceToken.
EXPECT_FALSE(thread_checker->CalledOnValidThread());
}
TEST(ThreadCheckerTest, DetachFromThread) {
ThreadCheckerImpl thread_checker;
thread_checker.DetachFromThread();
// Verify that CalledOnValidThread() returns true when called on a different
// thread after a call to DetachFromThread().
RunCallbackOnNewThreadSynchronously(
Bind(&ExpectCalledOnValidThread, Unretained(&thread_checker)));
EXPECT_FALSE(thread_checker.CalledOnValidThread());
}
TEST(ThreadCheckerTest, DetachFromThreadWithSequenceToken) {
ThreadTaskRunnerHandle thread_task_runner_handle(
MakeRefCounted<TestSimpleTaskRunner>());
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
ThreadCheckerImpl thread_checker;
thread_checker.DetachFromThread();
// Verify that CalledOnValidThread() returns true when called on a different
// thread after a call to DetachFromThread().
RunCallbackOnNewThreadSynchronously(
Bind(&ExpectCalledOnValidThread, Unretained(&thread_checker)));
EXPECT_FALSE(thread_checker.CalledOnValidThread());
}
namespace {
// This fixture is a helper for unit testing the thread checker macros as it is
// not possible to inline ExpectDeathOnOtherThread() and
// ExpectNoDeathOnOtherThreadAfterDetach() as lambdas since binding
// |Unretained(&my_sequence_checker)| wouldn't compile on non-dcheck builds
// where it won't be defined.
class ThreadCheckerMacroTest : public testing::Test {
public:
ThreadCheckerMacroTest() = default;
void ExpectDeathOnOtherThread() {
#if DCHECK_IS_ON()
EXPECT_DCHECK_DEATH({ DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_); });
#else
// Happily no-ops on non-dcheck builds.
DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_);
#endif
}
void ExpectNoDeathOnOtherThreadAfterDetach() {
DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_);
DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_)
<< "Make sure it compiles when DCHECK is off";
}
protected:
THREAD_CHECKER(my_thread_checker_);
private:
DISALLOW_COPY_AND_ASSIGN(ThreadCheckerMacroTest);
};
} // namespace
TEST_F(ThreadCheckerMacroTest, Macros) {
THREAD_CHECKER(my_thread_checker);
RunCallbackOnNewThreadSynchronously(Bind(
&ThreadCheckerMacroTest::ExpectDeathOnOtherThread, Unretained(this)));
DETACH_FROM_THREAD(my_thread_checker_);
RunCallbackOnNewThreadSynchronously(
Bind(&ThreadCheckerMacroTest::ExpectNoDeathOnOtherThreadAfterDetach,
Unretained(this)));
}
} // namespace base