1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_BlockingResourceBase_h 8 #define mozilla_BlockingResourceBase_h 9 10 #include "mozilla/MemoryReporting.h" 11 #include "mozilla/ThreadLocal.h" 12 13 #include "nscore.h" 14 #include "nsDebug.h" 15 16 #include "prtypes.h" 17 18 #ifdef DEBUG 19 20 // NB: Comment this out to enable callstack tracking. 21 # define MOZ_CALLSTACK_DISABLED 22 23 # include "prinit.h" 24 25 # ifndef MOZ_CALLSTACK_DISABLED 26 # include "mozilla/Maybe.h" 27 # include "nsTArray.h" 28 # endif 29 30 #endif 31 32 // 33 // This header is not meant to be included by client code. 34 // 35 36 namespace mozilla { 37 38 #ifdef DEBUG 39 template <class T> 40 class DeadlockDetector; 41 #endif 42 43 /** 44 * BlockingResourceBase 45 * Base class of resources that might block clients trying to acquire them. 46 * Does debugging and deadlock detection in DEBUG builds. 47 **/ 48 class BlockingResourceBase { 49 public: 50 // Needs to be kept in sync with kResourceTypeNames. 51 enum BlockingResourceType { 52 eMutex, 53 eReentrantMonitor, 54 eCondVar, 55 eRecursiveMutex 56 }; 57 58 /** 59 * kResourceTypeName 60 * Human-readable version of BlockingResourceType enum. 61 */ 62 static const char* const kResourceTypeName[]; 63 64 #ifdef DEBUG 65 66 static size_t SizeOfDeadlockDetector(MallocSizeOf aMallocSizeOf); 67 68 /** 69 * Print 70 * Write a description of this blocking resource to |aOut|. If 71 * the resource appears to be currently acquired, the current 72 * acquisition context is printed and true is returned. 73 * Otherwise, we print the context from |aFirstSeen|, the 74 * first acquisition from which the code calling |Print()| 75 * became interested in us, and return false. 76 * 77 * *NOT* thread safe. Reads |mAcquisitionContext| without 78 * synchronization, but this will not cause correctness 79 * problems. 80 * 81 * FIXME bug 456272: hack alert: because we can't write call 82 * contexts into strings, all info is written to stderr, but 83 * only some info is written into |aOut| 84 */ 85 bool Print(nsACString& aOut) const; 86 SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)87 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { 88 // NB: |mName| is not reported as it's expected to be a static string. 89 // If we switch to a nsString it should be added to the tally. 90 // |mChainPrev| is not reported because its memory is not owned. 91 size_t n = aMallocSizeOf(this); 92 return n; 93 } 94 95 // ``DDT'' = ``Deadlock Detector Type'' 96 typedef DeadlockDetector<BlockingResourceBase> DDT; 97 98 protected: 99 # ifdef MOZ_CALLSTACK_DISABLED 100 typedef bool AcquisitionState; 101 # else 102 // Using maybe to use emplacement as the acquisition state flag; we may not 103 // always get a stack trace because of possible stack walk suppression or 104 // errors, hence can't use !IsEmpty() on the array itself as indication. 105 static size_t const kAcquisitionStateStackSize = 24; 106 typedef Maybe<AutoTArray<void*, kAcquisitionStateStackSize> > 107 AcquisitionState; 108 # endif 109 110 /** 111 * BlockingResourceBase 112 * Initialize this blocking resource. Also hooks the resource into 113 * instrumentation code. 114 * 115 * Thread safe. 116 * 117 * @param aName A meaningful, unique name that can be used in 118 * error messages, et al. 119 * @param aType The specific type of |this|, if any. 120 **/ 121 BlockingResourceBase(const char* aName, BlockingResourceType aType); 122 123 ~BlockingResourceBase(); 124 125 /** 126 * CheckAcquire 127 * 128 * Thread safe. 129 **/ 130 void CheckAcquire(); 131 132 /** 133 * Acquire 134 * 135 * *NOT* thread safe. Requires ownership of underlying resource. 136 **/ 137 void Acquire(); // NS_NEEDS_RESOURCE(this) 138 139 /** 140 * Release 141 * Remove this resource from the current thread's acquisition chain. 142 * The resource does not have to be at the front of the chain, although 143 * it is confusing to release resources in a different order than they 144 * are acquired. This generates a warning. 145 * 146 * *NOT* thread safe. Requires ownership of underlying resource. 147 **/ 148 void Release(); // NS_NEEDS_RESOURCE(this) 149 150 /** 151 * ResourceChainFront 152 * 153 * Thread safe. 154 * 155 * @return the front of the resource acquisition chain, i.e., the last 156 * resource acquired. 157 */ ResourceChainFront()158 static BlockingResourceBase* ResourceChainFront() { 159 return sResourceAcqnChainFront.get(); 160 } 161 162 /** 163 * ResourceChainPrev 164 * 165 * *NOT* thread safe. Requires ownership of underlying resource. 166 */ ResourceChainPrev(const BlockingResourceBase * aResource)167 static BlockingResourceBase* ResourceChainPrev( 168 const BlockingResourceBase* aResource) { 169 return aResource->mChainPrev; 170 } // NS_NEEDS_RESOURCE(this) 171 172 /** 173 * ResourceChainAppend 174 * Set |this| to the front of the resource acquisition chain, and link 175 * |this| to |aPrev|. 176 * 177 * *NOT* thread safe. Requires ownership of underlying resource. 178 */ ResourceChainAppend(BlockingResourceBase * aPrev)179 void ResourceChainAppend(BlockingResourceBase* aPrev) { 180 mChainPrev = aPrev; 181 sResourceAcqnChainFront.set(this); 182 } // NS_NEEDS_RESOURCE(this) 183 184 /** 185 * ResourceChainRemove 186 * Remove |this| from the front of the resource acquisition chain. 187 * 188 * *NOT* thread safe. Requires ownership of underlying resource. 189 */ ResourceChainRemove()190 void ResourceChainRemove() { 191 NS_ASSERTION(this == ResourceChainFront(), "not at chain front"); 192 sResourceAcqnChainFront.set(mChainPrev); 193 } // NS_NEEDS_RESOURCE(this) 194 195 /** 196 * TakeAcquisitionState 197 * Return whether or not this resource was acquired and mark the resource 198 * as not acquired for subsequent uses. 199 * 200 * *NOT* thread safe. Requires ownership of underlying resource. 201 */ TakeAcquisitionState()202 AcquisitionState TakeAcquisitionState() { 203 # ifdef MOZ_CALLSTACK_DISABLED 204 bool acquired = mAcquired; 205 ClearAcquisitionState(); 206 return acquired; 207 # else 208 return mAcquired.take(); 209 # endif 210 } 211 212 /** 213 * SetAcquisitionState 214 * Set whether or not this resource was acquired. 215 * 216 * *NOT* thread safe. Requires ownership of underlying resource. 217 */ SetAcquisitionState(AcquisitionState && aAcquisitionState)218 void SetAcquisitionState(AcquisitionState&& aAcquisitionState) { 219 mAcquired = std::move(aAcquisitionState); 220 } 221 222 /** 223 * ClearAcquisitionState 224 * Indicate this resource is not acquired. 225 * 226 * *NOT* thread safe. Requires ownership of underlying resource. 227 */ ClearAcquisitionState()228 void ClearAcquisitionState() { 229 # ifdef MOZ_CALLSTACK_DISABLED 230 mAcquired = false; 231 # else 232 mAcquired.reset(); 233 # endif 234 } 235 236 /** 237 * IsAcquired 238 * Indicates if this resource is acquired. 239 * 240 * *NOT* thread safe. Requires ownership of underlying resource. 241 */ IsAcquired()242 bool IsAcquired() const { return (bool)mAcquired; } 243 244 /** 245 * mChainPrev 246 * A series of resource acquisitions creates a chain of orders. This 247 * chain is implemented as a linked list; |mChainPrev| points to the 248 * resource most recently Acquire()'d before this one. 249 **/ 250 BlockingResourceBase* mChainPrev; 251 252 private: 253 /** 254 * mName 255 * A descriptive name for this resource. Used in error 256 * messages etc. 257 */ 258 const char* mName; 259 260 /** 261 * mType 262 * The more specific type of this resource. Used to implement 263 * special semantics (e.g., reentrancy of monitors). 264 **/ 265 BlockingResourceType mType; 266 267 /** 268 * mAcquired 269 * Indicates if this resource is currently acquired. 270 */ 271 AcquisitionState mAcquired; 272 273 # ifndef MOZ_CALLSTACK_DISABLED 274 /** 275 * mFirstSeen 276 * Inidicates where this resource was first acquired. 277 */ 278 AcquisitionState mFirstSeen; 279 # endif 280 281 /** 282 * sCallOnce 283 * Ensures static members are initialized only once, and in a 284 * thread-safe way. 285 */ 286 static PRCallOnceType sCallOnce; 287 288 /** 289 * Thread-private pointer to the front of each thread's resource 290 * acquisition chain. 291 */ 292 static MOZ_THREAD_LOCAL(BlockingResourceBase*) sResourceAcqnChainFront; 293 294 /** 295 * sDeadlockDetector 296 * Does as named. 297 */ 298 static DDT* sDeadlockDetector; 299 300 /** 301 * InitStatics 302 * Inititialize static members of BlockingResourceBase that can't 303 * be statically initialized. 304 * 305 * *NOT* thread safe. 306 */ 307 static PRStatus InitStatics(); 308 309 /** 310 * Shutdown 311 * Free static members. 312 * 313 * *NOT* thread safe. 314 */ 315 static void Shutdown(); 316 317 static void StackWalkCallback(uint32_t aFrameNumber, void* aPc, void* aSp, 318 void* aClosure); 319 static void GetStackTrace(AcquisitionState& aState, 320 const void* aFirstFramePC); 321 322 # ifdef MOZILLA_INTERNAL_API 323 // so it can call BlockingResourceBase::Shutdown() 324 friend void LogTerm(); 325 # endif // ifdef MOZILLA_INTERNAL_API 326 327 #else // non-DEBUG implementation 328 BlockingResourceBase(const char * aName,BlockingResourceType aType)329 BlockingResourceBase(const char* aName, BlockingResourceType aType) {} 330 ~BlockingResourceBase()331 ~BlockingResourceBase() {} 332 333 #endif 334 }; 335 336 } // namespace mozilla 337 338 #endif // mozilla_BlockingResourceBase_h 339