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 128namespace 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) 134struct BASE_API AsserterBase { 135 virtual ~AsserterBase() {} 136 virtual void warn() = 0; 137}; 138 139struct BASE_API DCheckAsserter : public AsserterBase { 140 virtual ~DCheckAsserter() {} 141 virtual void warn(); 142}; 143 144class 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