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