246 lines
7.8 KiB
C++
246 lines
7.8 KiB
C++
// 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
|