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