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