|  | // 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 |