1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/logging.h" 6#include "base/macros.h" 7#include "base/memory/scoped_ptr.h" 8#include "base/threading/simple_thread.h" 9#include "base/threading/thread_checker.h" 10#include "testing/gtest/include/gtest/gtest.h" 11 12// Duplicated from base/threading/thread_checker.h so that we can be 13// good citizens there and undef the macro. 14#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) 15#define ENABLE_THREAD_CHECKER 1 16#else 17#define ENABLE_THREAD_CHECKER 0 18#endif 19 20namespace base { 21 22namespace { 23 24// Simple class to exercise the basics of ThreadChecker. 25// Both the destructor and DoStuff should verify that they were 26// called on the same thread as the constructor. 27class ThreadCheckerClass : public ThreadChecker { 28 public: 29 ThreadCheckerClass() {} 30 31 // Verifies that it was called on the same thread as the constructor. 32 void DoStuff() { 33 DCHECK(CalledOnValidThread()); 34 } 35 36 void DetachFromThread() { 37 ThreadChecker::DetachFromThread(); 38 } 39 40 static void MethodOnDifferentThreadImpl(); 41 static void DetachThenCallFromDifferentThreadImpl(); 42 43 private: 44 DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass); 45}; 46 47// Calls ThreadCheckerClass::DoStuff on another thread. 48class CallDoStuffOnThread : public base::SimpleThread { 49 public: 50 explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class) 51 : SimpleThread("call_do_stuff_on_thread"), 52 thread_checker_class_(thread_checker_class) { 53 } 54 55 void Run() override { thread_checker_class_->DoStuff(); } 56 57 private: 58 ThreadCheckerClass* thread_checker_class_; 59 60 DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread); 61}; 62 63// Deletes ThreadCheckerClass on a different thread. 64class DeleteThreadCheckerClassOnThread : public base::SimpleThread { 65 public: 66 explicit DeleteThreadCheckerClassOnThread( 67 ThreadCheckerClass* thread_checker_class) 68 : SimpleThread("delete_thread_checker_class_on_thread"), 69 thread_checker_class_(thread_checker_class) { 70 } 71 72 void Run() override { thread_checker_class_.reset(); } 73 74 private: 75 scoped_ptr<ThreadCheckerClass> thread_checker_class_; 76 77 DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread); 78}; 79 80} // namespace 81 82TEST(ThreadCheckerTest, CallsAllowedOnSameThread) { 83 scoped_ptr<ThreadCheckerClass> thread_checker_class( 84 new ThreadCheckerClass); 85 86 // Verify that DoStuff doesn't assert. 87 thread_checker_class->DoStuff(); 88 89 // Verify that the destructor doesn't assert. 90 thread_checker_class.reset(); 91} 92 93TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) { 94 scoped_ptr<ThreadCheckerClass> thread_checker_class( 95 new ThreadCheckerClass); 96 97 // Verify that the destructor doesn't assert 98 // when called on a different thread. 99 DeleteThreadCheckerClassOnThread delete_on_thread( 100 thread_checker_class.release()); 101 102 delete_on_thread.Start(); 103 delete_on_thread.Join(); 104} 105 106TEST(ThreadCheckerTest, DetachFromThread) { 107 scoped_ptr<ThreadCheckerClass> thread_checker_class( 108 new ThreadCheckerClass); 109 110 // Verify that DoStuff doesn't assert when called on a different thread after 111 // a call to DetachFromThread. 112 thread_checker_class->DetachFromThread(); 113 CallDoStuffOnThread call_on_thread(thread_checker_class.get()); 114 115 call_on_thread.Start(); 116 call_on_thread.Join(); 117} 118 119#if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER 120 121void ThreadCheckerClass::MethodOnDifferentThreadImpl() { 122 scoped_ptr<ThreadCheckerClass> thread_checker_class( 123 new ThreadCheckerClass); 124 125 // DoStuff should assert in debug builds only when called on a 126 // different thread. 127 CallDoStuffOnThread call_on_thread(thread_checker_class.get()); 128 129 call_on_thread.Start(); 130 call_on_thread.Join(); 131} 132 133#if ENABLE_THREAD_CHECKER 134TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) { 135 ASSERT_DEATH({ 136 ThreadCheckerClass::MethodOnDifferentThreadImpl(); 137 }, ""); 138} 139#else 140TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) { 141 ThreadCheckerClass::MethodOnDifferentThreadImpl(); 142} 143#endif // ENABLE_THREAD_CHECKER 144 145void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() { 146 scoped_ptr<ThreadCheckerClass> thread_checker_class( 147 new ThreadCheckerClass); 148 149 // DoStuff doesn't assert when called on a different thread 150 // after a call to DetachFromThread. 151 thread_checker_class->DetachFromThread(); 152 CallDoStuffOnThread call_on_thread(thread_checker_class.get()); 153 154 call_on_thread.Start(); 155 call_on_thread.Join(); 156 157 // DoStuff should assert in debug builds only after moving to 158 // another thread. 159 thread_checker_class->DoStuff(); 160} 161 162#if ENABLE_THREAD_CHECKER 163TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) { 164 ASSERT_DEATH({ 165 ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); 166 }, ""); 167} 168#else 169TEST(ThreadCheckerTest, DetachFromThreadInRelease) { 170 ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); 171} 172#endif // ENABLE_THREAD_CHECKER 173 174#endif // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER 175 176// Just in case we ever get lumped together with other compilation units. 177#undef ENABLE_THREAD_CHECKER 178 179} // namespace base 180