1 // Copyright (c) 2011 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 #ifndef BASE_THREADING_THREAD_COLLISION_WARNER_H_ 6 #define BASE_THREADING_THREAD_COLLISION_WARNER_H_ 7 #pragma once 8 9 #include <memory> 10 11 #include "base/base_api.h" 12 #include "base/atomicops.h" 13 14 // A helper class alongside macros to be used to verify assumptions about thread 15 // safety of a class. 16 // 17 // Example: Queue implementation non thread-safe but still usable if clients 18 // are synchronized somehow. 19 // 20 // In this case the macro DFAKE_SCOPED_LOCK has to be 21 // used, it checks that if a thread is inside the push/pop then 22 // noone else is still inside the pop/push 23 // 24 // class NonThreadSafeQueue { 25 // public: 26 // ... 27 // void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... } 28 // int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... } 29 // ... 30 // private: 31 // DFAKE_MUTEX(push_pop_); 32 // }; 33 // 34 // 35 // Example: Queue implementation non thread-safe but still usable if clients 36 // are synchronized somehow, it calls a method to "protect" from 37 // a "protected" method 38 // 39 // In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK 40 // has to be used, it checks that if a thread is inside the push/pop 41 // then noone else is still inside the pop/push 42 // 43 // class NonThreadSafeQueue { 44 // public: 45 // void push(int) { 46 // DFAKE_SCOPED_LOCK(push_pop_); 47 // ... 48 // } 49 // int pop() { 50 // DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); 51 // bar(); 52 // ... 53 // } 54 // void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... } 55 // ... 56 // private: 57 // DFAKE_MUTEX(push_pop_); 58 // }; 59 // 60 // 61 // Example: Queue implementation not usable even if clients are synchronized, 62 // so only one thread in the class life cycle can use the two members 63 // push/pop. 64 // 65 // In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the 66 // specified 67 // critical section the first time a thread enters push or pop, from 68 // that time on only that thread is allowed to execute push or pop. 69 // 70 // class NonThreadSafeQueue { 71 // public: 72 // ... 73 // void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } 74 // int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } 75 // ... 76 // private: 77 // DFAKE_MUTEX(push_pop_); 78 // }; 79 // 80 // 81 // Example: Class that has to be contructed/destroyed on same thread, it has 82 // a "shareable" method (with external syncronization) and a not 83 // shareable method (even with external synchronization). 84 // 85 // In this case 3 Critical sections have to be defined 86 // 87 // class ExoticClass { 88 // public: 89 // ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } 90 // ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } 91 // 92 // void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... } 93 // void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } 94 // ... 95 // private: 96 // DFAKE_MUTEX(ctor_dtor_); 97 // DFAKE_MUTEX(shareable_section_); 98 // }; 99 100 101 #if !defined(NDEBUG) 102 103 // Defines a class member that acts like a mutex. It is used only as a 104 // verification tool. 105 #define DFAKE_MUTEX(obj) \ 106 mutable base::ThreadCollisionWarner obj 107 // Asserts the call is never called simultaneously in two threads. Used at 108 // member function scope. 109 #define DFAKE_SCOPED_LOCK(obj) \ 110 base::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj) 111 // Asserts the call is never called simultaneously in two threads. Used at 112 // member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks. 113 #define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \ 114 base::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj) 115 // Asserts the code is always executed in the same thread. 116 #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \ 117 base::ThreadCollisionWarner::Check check_##obj(&obj) 118 119 #else 120 121 #define DFAKE_MUTEX(obj) 122 #define DFAKE_SCOPED_LOCK(obj) ((void)0) 123 #define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0) 124 #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0) 125 126 #endif 127 128 namespace base { 129 130 // The class ThreadCollisionWarner uses an Asserter to notify the collision 131 // AsserterBase is the interfaces and DCheckAsserter is the default asserter 132 // used. During the unit tests is used another class that doesn't "DCHECK" 133 // in case of collision (check thread_collision_warner_unittests.cc) 134 struct BASE_API AsserterBase { 135 virtual ~AsserterBase() {} 136 virtual void warn() = 0; 137 }; 138 139 struct BASE_API DCheckAsserter : public AsserterBase { 140 virtual ~DCheckAsserter() {} 141 virtual void warn(); 142 }; 143 144 class BASE_API ThreadCollisionWarner { 145 public: 146 // The parameter asserter is there only for test purpose 147 ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter()) 148 : valid_thread_id_(0), 149 counter_(0), 150 asserter_(asserter) {} 151 152 ~ThreadCollisionWarner() { 153 delete asserter_; 154 } 155 156 // This class is meant to be used through the macro 157 // DFAKE_SCOPED_LOCK_THREAD_LOCKED 158 // it doesn't leave the critical section, as opposed to ScopedCheck, 159 // because the critical section being pinned is allowed to be used only 160 // from one thread 161 class BASE_API Check { 162 public: 163 explicit Check(ThreadCollisionWarner* warner) 164 : warner_(warner) { 165 warner_->EnterSelf(); 166 } 167 168 ~Check() {} 169 170 private: 171 ThreadCollisionWarner* warner_; 172 173 DISALLOW_COPY_AND_ASSIGN(Check); 174 }; 175 176 // This class is meant to be used through the macro 177 // DFAKE_SCOPED_LOCK 178 class BASE_API ScopedCheck { 179 public: 180 explicit ScopedCheck(ThreadCollisionWarner* warner) 181 : warner_(warner) { 182 warner_->Enter(); 183 } 184 185 ~ScopedCheck() { 186 warner_->Leave(); 187 } 188 189 private: 190 ThreadCollisionWarner* warner_; 191 192 DISALLOW_COPY_AND_ASSIGN(ScopedCheck); 193 }; 194 195 // This class is meant to be used through the macro 196 // DFAKE_SCOPED_RECURSIVE_LOCK 197 class BASE_API ScopedRecursiveCheck { 198 public: 199 explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner) 200 : warner_(warner) { 201 warner_->EnterSelf(); 202 } 203 204 ~ScopedRecursiveCheck() { 205 warner_->Leave(); 206 } 207 208 private: 209 ThreadCollisionWarner* warner_; 210 211 DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck); 212 }; 213 214 private: 215 // This method stores the current thread identifier and does a DCHECK 216 // if a another thread has already done it, it is safe if same thread 217 // calls this multiple time (recursion allowed). 218 void EnterSelf(); 219 220 // Same as EnterSelf but recursion is not allowed. 221 void Enter(); 222 223 // Removes the thread_id stored in order to allow other threads to 224 // call EnterSelf or Enter. 225 void Leave(); 226 227 // This stores the thread id that is inside the critical section, if the 228 // value is 0 then no thread is inside. 229 volatile subtle::Atomic32 valid_thread_id_; 230 231 // Counter to trace how many time a critical section was "pinned" 232 // (when allowed) in order to unpin it when counter_ reaches 0. 233 volatile subtle::Atomic32 counter_; 234 235 // Here only for class unit tests purpose, during the test I need to not 236 // DCHECK but notify the collision with something else. 237 AsserterBase* asserter_; 238 239 DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner); 240 }; 241 242 } // namespace base 243 244 #endif // BASE_THREADING_THREAD_COLLISION_WARNER_H_ 245