security/sandbox/chromium/base/threading/thread_collision_warner.h

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

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

mercurial