1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/base/thread_collision_warner.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,242 @@ 1.4 +// Copyright (c) 2008 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#ifndef BASE_THREAD_COLLISION_WARNER_H_ 1.9 +#define BASE_THREAD_COLLISION_WARNER_H_ 1.10 + 1.11 +#include <memory.h> 1.12 + 1.13 +#include "base/atomicops.h" 1.14 + 1.15 +// A helper class alongside macros to be used to verify assumptions about thread 1.16 +// safety of a class. 1.17 +// 1.18 +// Example: Queue implementation non thread-safe but still usable if clients 1.19 +// are synchronized somehow. 1.20 +// 1.21 +// In this case the macro DFAKE_SCOPED_LOCK has to be 1.22 +// used, it checks that if a thread is inside the push/pop then 1.23 +// noone else is still inside the pop/push 1.24 +// 1.25 +// class NonThreadSafeQueue { 1.26 +// public: 1.27 +// ... 1.28 +// void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... } 1.29 +// int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... } 1.30 +// ... 1.31 +// private: 1.32 +// DFAKE_MUTEX(push_pop_); 1.33 +// }; 1.34 +// 1.35 +// 1.36 +// Example: Queue implementation non thread-safe but still usable if clients 1.37 +// are synchronized somehow, it calls a method to "protect" from 1.38 +// a "protected" method 1.39 +// 1.40 +// In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK 1.41 +// has to be used, it checks that if a thread is inside the push/pop 1.42 +// then noone else is still inside the pop/push 1.43 +// 1.44 +// class NonThreadSafeQueue { 1.45 +// public: 1.46 +// void push(int) { 1.47 +// DFAKE_SCOPED_LOCK(push_pop_); 1.48 +// ... 1.49 +// } 1.50 +// int pop() { 1.51 +// DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); 1.52 +// bar(); 1.53 +// ... 1.54 +// } 1.55 +// void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... } 1.56 +// ... 1.57 +// private: 1.58 +// DFAKE_MUTEX(push_pop_); 1.59 +// }; 1.60 +// 1.61 +// 1.62 +// Example: Queue implementation not usable even if clients are synchronized, 1.63 +// so only one thread in the class life cycle can use the two members 1.64 +// push/pop. 1.65 +// 1.66 +// In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the 1.67 +// specified 1.68 +// critical section the first time a thread enters push or pop, from 1.69 +// that time on only that thread is allowed to execute push or pop. 1.70 +// 1.71 +// class NonThreadSafeQueue { 1.72 +// public: 1.73 +// ... 1.74 +// void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } 1.75 +// int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } 1.76 +// ... 1.77 +// private: 1.78 +// DFAKE_MUTEX(push_pop_); 1.79 +// }; 1.80 +// 1.81 +// 1.82 +// Example: Class that has to be contructed/destroyed on same thread, it has 1.83 +// a "shareable" method (with external syncronization) and a not 1.84 +// shareable method (even with external synchronization). 1.85 +// 1.86 +// In this case 3 Critical sections have to be defined 1.87 +// 1.88 +// class ExoticClass { 1.89 +// public: 1.90 +// ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } 1.91 +// ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } 1.92 +// 1.93 +// void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... } 1.94 +// void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } 1.95 +// ... 1.96 +// private: 1.97 +// DFAKE_MUTEX(ctor_dtor_); 1.98 +// DFAKE_MUTEX(shareable_section_); 1.99 +// }; 1.100 + 1.101 + 1.102 +#if !defined(NDEBUG) 1.103 + 1.104 +// Defines a class member that acts like a mutex. It is used only as a 1.105 +// verification tool. 1.106 +#define DFAKE_MUTEX(obj) \ 1.107 + mutable base::ThreadCollisionWarner obj; 1.108 +// Asserts the call is never called simultaneously in two threads. Used at 1.109 +// member function scope. 1.110 +#define DFAKE_SCOPED_LOCK(obj) \ 1.111 + base::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj) 1.112 +// Asserts the call is never called simultaneously in two threads. Used at 1.113 +// member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks. 1.114 +#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \ 1.115 + base::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj) 1.116 +// Asserts the code is always executed in the same thread. 1.117 +#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \ 1.118 + base::ThreadCollisionWarner::Check check_##obj(&obj) 1.119 + 1.120 +#else 1.121 + 1.122 +#define DFAKE_MUTEX(obj) 1.123 +#define DFAKE_SCOPED_LOCK(obj) ((void)0) 1.124 +#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0) 1.125 +#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0) 1.126 + 1.127 +#endif 1.128 + 1.129 +namespace base { 1.130 + 1.131 +// The class ThreadCollisionWarner uses an Asserter to notify the collision 1.132 +// AsserterBase is the interfaces and DCheckAsserter is the default asserter 1.133 +// used. During the unit tests is used another class that doesn't "DCHECK" 1.134 +// in case of collision (check thread_collision_warner_unittests.cc) 1.135 +struct AsserterBase { 1.136 + virtual ~AsserterBase() {} 1.137 + virtual void warn() = 0; 1.138 +}; 1.139 + 1.140 +struct DCheckAsserter : public AsserterBase { 1.141 + virtual ~DCheckAsserter() {} 1.142 + virtual void warn(); 1.143 +}; 1.144 + 1.145 +class ThreadCollisionWarner { 1.146 + public: 1.147 + // The parameter asserter is there only for test purpose 1.148 + ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter()) 1.149 + : valid_thread_id_(0), 1.150 + counter_(0), 1.151 + asserter_(asserter) {} 1.152 + 1.153 + ~ThreadCollisionWarner() { 1.154 + delete asserter_; 1.155 + } 1.156 + 1.157 + // This class is meant to be used through the macro 1.158 + // DFAKE_SCOPED_LOCK_THREAD_LOCKED 1.159 + // it doesn't leave the critical section, as opposed to ScopedCheck, 1.160 + // because the critical section being pinned is allowed to be used only 1.161 + // from one thread 1.162 + class Check { 1.163 + public: 1.164 + explicit Check(ThreadCollisionWarner* warner) 1.165 + : warner_(warner) { 1.166 + warner_->EnterSelf(); 1.167 + } 1.168 + 1.169 + ~Check() {} 1.170 + 1.171 + private: 1.172 + ThreadCollisionWarner* warner_; 1.173 + 1.174 + DISALLOW_COPY_AND_ASSIGN(Check); 1.175 + }; 1.176 + 1.177 + // This class is meant to be used through the macro 1.178 + // DFAKE_SCOPED_LOCK 1.179 + class ScopedCheck { 1.180 + public: 1.181 + explicit ScopedCheck(ThreadCollisionWarner* warner) 1.182 + : warner_(warner) { 1.183 + warner_->Enter(); 1.184 + } 1.185 + 1.186 + ~ScopedCheck() { 1.187 + warner_->Leave(); 1.188 + } 1.189 + 1.190 + private: 1.191 + ThreadCollisionWarner* warner_; 1.192 + 1.193 + DISALLOW_COPY_AND_ASSIGN(ScopedCheck); 1.194 + }; 1.195 + 1.196 + // This class is meant to be used through the macro 1.197 + // DFAKE_SCOPED_RECURSIVE_LOCK 1.198 + class ScopedRecursiveCheck { 1.199 + public: 1.200 + explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner) 1.201 + : warner_(warner) { 1.202 + warner_->EnterSelf(); 1.203 + } 1.204 + 1.205 + ~ScopedRecursiveCheck() { 1.206 + warner_->Leave(); 1.207 + } 1.208 + 1.209 + private: 1.210 + ThreadCollisionWarner* warner_; 1.211 + 1.212 + DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck); 1.213 + }; 1.214 + 1.215 + private: 1.216 + // This method stores the current thread identifier and does a DCHECK 1.217 + // if a another thread has already done it, it is safe if same thread 1.218 + // calls this multiple time (recursion allowed). 1.219 + void EnterSelf(); 1.220 + 1.221 + // Same as EnterSelf but recursion is not allowed. 1.222 + void Enter(); 1.223 + 1.224 + // Removes the thread_id stored in order to allow other threads to 1.225 + // call EnterSelf or Enter. 1.226 + void Leave(); 1.227 + 1.228 + // This stores the thread id that is inside the critical section, if the 1.229 + // value is 0 then no thread is inside. 1.230 + volatile subtle::Atomic32 valid_thread_id_; 1.231 + 1.232 + // Counter to trace how many time a critical section was "pinned" 1.233 + // (when allowed) in order to unpin it when counter_ reaches 0. 1.234 + volatile subtle::Atomic32 counter_; 1.235 + 1.236 + // Here only for class unit tests purpose, during the test I need to not 1.237 + // DCHECK but notify the collision with something else. 1.238 + AsserterBase* asserter_; 1.239 + 1.240 + DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner); 1.241 +}; 1.242 + 1.243 +} // namespace base 1.244 + 1.245 +#endif // BASE_THREAD_COLLISION_WARNER_H_