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 20 namespace base { 21 22 namespace { 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. 27 class 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. 48 class 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. 64 class 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 82 TEST(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 93 TEST(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 106 TEST(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 121 void 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 134 TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) { 135 ASSERT_DEATH({ 136 ThreadCheckerClass::MethodOnDifferentThreadImpl(); 137 }, ""); 138 } 139 #else 140 TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) { 141 ThreadCheckerClass::MethodOnDifferentThreadImpl(); 142 } 143 #endif // ENABLE_THREAD_CHECKER 144 145 void 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 163 TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) { 164 ASSERT_DEATH({ 165 ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); 166 }, ""); 167 } 168 #else 169 TEST(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