1 // Copyright 2012 The Chromium Authors 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 8 #include "base/atomicops.h" 9 #include "base/base_export.h" 10 #include "base/compiler_specific.h" 11 #include "base/macros/uniquify.h" 12 #include "base/memory/raw_ptr.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 synchronization) 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 #if !defined(NDEBUG) 101 102 // Defines a class member that acts like a mutex. It is used only as a 103 // verification tool. 104 #define DFAKE_MUTEX(obj) \ 105 mutable base::ThreadCollisionWarner obj 106 // Asserts the call is never called simultaneously in two threads. Used at 107 // member function scope. 108 #define DFAKE_SCOPED_LOCK(obj) \ 109 base::ThreadCollisionWarner::ScopedCheck BASE_UNIQUIFY(s_check_)(&obj) 110 // Asserts the call is never called simultaneously in two threads. Used at 111 // member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks. 112 #define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \ 113 base::ThreadCollisionWarner::ScopedRecursiveCheck BASE_UNIQUIFY(sr_check)( \ 114 &obj) 115 // Asserts the code is always executed in the same thread. 116 #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \ 117 base::ThreadCollisionWarner::Check BASE_UNIQUIFY(check_)(&obj) 118 119 #else 120 121 #define DFAKE_MUTEX(obj) typedef void InternalFakeMutexType##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_EXPORT AsserterBase { 135 virtual ~AsserterBase() = default; 136 virtual void warn() = 0; 137 }; 138 139 struct BASE_EXPORT DCheckAsserter : public AsserterBase { 140 ~DCheckAsserter() override = default; 141 void warn() override; 142 }; 143 144 class BASE_EXPORT ThreadCollisionWarner { 145 public: 146 // The parameter asserter is there only for test purpose 147 explicit ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter()) 148 : valid_thread_id_(0), 149 counter_(0), 150 asserter_(asserter) {} 151 152 ThreadCollisionWarner(const ThreadCollisionWarner&) = delete; 153 ThreadCollisionWarner& operator=(const ThreadCollisionWarner&) = delete; 154 ~ThreadCollisionWarner()155 ~ThreadCollisionWarner() { asserter_.ClearAndDelete(); } 156 157 // This class is meant to be used through the macro 158 // DFAKE_SCOPED_LOCK_THREAD_LOCKED 159 // it doesn't leave the critical section, as opposed to ScopedCheck, 160 // because the critical section being pinned is allowed to be used only 161 // from one thread 162 class BASE_EXPORT Check { 163 public: Check(ThreadCollisionWarner * warner)164 explicit Check(ThreadCollisionWarner* warner) 165 : warner_(warner) { 166 warner_->EnterSelf(); 167 } 168 169 Check(const Check&) = delete; 170 Check& operator=(const Check&) = delete; 171 172 ~Check() = default; 173 174 private: 175 raw_ptr<ThreadCollisionWarner> warner_; 176 }; 177 178 // This class is meant to be used through the macro 179 // DFAKE_SCOPED_LOCK 180 class BASE_EXPORT ScopedCheck { 181 public: ScopedCheck(ThreadCollisionWarner * warner)182 explicit ScopedCheck(ThreadCollisionWarner* warner) 183 : warner_(warner) { 184 warner_->Enter(); 185 } 186 187 ScopedCheck(const ScopedCheck&) = delete; 188 ScopedCheck& operator=(const ScopedCheck&) = delete; 189 ~ScopedCheck()190 ~ScopedCheck() { 191 warner_->Leave(); 192 } 193 194 private: 195 raw_ptr<ThreadCollisionWarner> warner_; 196 }; 197 198 // This class is meant to be used through the macro 199 // DFAKE_SCOPED_RECURSIVE_LOCK 200 class BASE_EXPORT ScopedRecursiveCheck { 201 public: ScopedRecursiveCheck(ThreadCollisionWarner * warner)202 explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner) 203 : warner_(warner) { 204 warner_->EnterSelf(); 205 } 206 207 ScopedRecursiveCheck(const ScopedRecursiveCheck&) = delete; 208 ScopedRecursiveCheck& operator=(const ScopedRecursiveCheck&) = delete; 209 ~ScopedRecursiveCheck()210 ~ScopedRecursiveCheck() { 211 warner_->Leave(); 212 } 213 214 private: 215 raw_ptr<ThreadCollisionWarner> warner_; 216 }; 217 218 private: 219 // This method stores the current thread identifier and does a DCHECK 220 // if a another thread has already done it, it is safe if same thread 221 // calls this multiple time (recursion allowed). 222 void EnterSelf(); 223 224 // Same as EnterSelf but recursion is not allowed. 225 void Enter(); 226 227 // Removes the thread_id stored in order to allow other threads to 228 // call EnterSelf or Enter. 229 void Leave(); 230 231 // This stores the thread id that is inside the critical section, if the 232 // value is 0 then no thread is inside. 233 volatile subtle::Atomic32 valid_thread_id_; 234 235 // Counter to trace how many time a critical section was "pinned" 236 // (when allowed) in order to unpin it when counter_ reaches 0. 237 volatile subtle::Atomic32 counter_; 238 239 // Here only for class unit tests purpose, during the test I need to not 240 // DCHECK but notify the collision with something else. 241 raw_ptr<AsserterBase> asserter_; 242 }; 243 244 } // namespace base 245 246 #endif // BASE_THREADING_THREAD_COLLISION_WARNER_H_ 247