|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: sw=4 ts=4 et : |
|
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 "prlog.h" |
|
12 |
|
13 #include "nscore.h" |
|
14 #include "nsDebug.h" |
|
15 #include "nsError.h" |
|
16 #include "nsISupportsImpl.h" |
|
17 |
|
18 #ifdef DEBUG |
|
19 #include "prinit.h" |
|
20 #include "prthread.h" |
|
21 |
|
22 #include "nsStringGlue.h" |
|
23 |
|
24 #include "mozilla/DeadlockDetector.h" |
|
25 #include "nsXPCOM.h" |
|
26 #endif |
|
27 |
|
28 // |
|
29 // This header is not meant to be included by client code. |
|
30 // |
|
31 |
|
32 namespace mozilla { |
|
33 |
|
34 |
|
35 /** |
|
36 * BlockingResourceBase |
|
37 * Base class of resources that might block clients trying to acquire them. |
|
38 * Does debugging and deadlock detection in DEBUG builds. |
|
39 **/ |
|
40 class NS_COM_GLUE BlockingResourceBase |
|
41 { |
|
42 public: |
|
43 // Needs to be kept in sync with kResourceTypeNames. |
|
44 enum BlockingResourceType { eMutex, eReentrantMonitor, eCondVar }; |
|
45 |
|
46 /** |
|
47 * kResourceTypeName |
|
48 * Human-readable version of BlockingResourceType enum. |
|
49 */ |
|
50 static const char* const kResourceTypeName[]; |
|
51 |
|
52 |
|
53 #ifdef DEBUG |
|
54 |
|
55 private: |
|
56 // forward declaration for the following typedef |
|
57 struct DeadlockDetectorEntry; |
|
58 |
|
59 // ``DDT'' = ``Deadlock Detector Type'' |
|
60 typedef DeadlockDetector<DeadlockDetectorEntry> DDT; |
|
61 |
|
62 /** |
|
63 * DeadlockDetectorEntry |
|
64 * We free BlockingResources, but we never free entries in the |
|
65 * deadlock detector. This struct outlives its BlockingResource |
|
66 * and preserves all the state needed to print subsequent |
|
67 * error messages. |
|
68 * |
|
69 * These objects are owned by the deadlock detector. |
|
70 */ |
|
71 struct DeadlockDetectorEntry |
|
72 { |
|
73 DeadlockDetectorEntry(const char* aName, |
|
74 BlockingResourceType aType) : |
|
75 mName(aName), |
|
76 mType(aType), |
|
77 mAcquisitionContext(CallStack::kNone) |
|
78 { |
|
79 NS_ABORT_IF_FALSE(mName, "Name must be nonnull"); |
|
80 } |
|
81 |
|
82 /** |
|
83 * Print |
|
84 * Write a description of this blocking resource to |out|. If |
|
85 * the resource appears to be currently acquired, the current |
|
86 * acquisition context is printed and true is returned. |
|
87 * Otherwise, we print the context from |aFirstSeen|, the |
|
88 * first acquisition from which the code calling |Print()| |
|
89 * became interested in us, and return false. |Print()| can |
|
90 * be forced to print the context from |aFirstSeen| regardless |
|
91 * by passing |aPrintFirstSeenCx=true|. |
|
92 * |
|
93 * *NOT* thread safe. Reads |mAcquisitionContext| without |
|
94 * synchronization, but this will not cause correctness |
|
95 * problems. |
|
96 * |
|
97 * FIXME bug 456272: hack alert: because we can't write call |
|
98 * contexts into strings, all info is written to stderr, but |
|
99 * only some info is written into |out| |
|
100 */ |
|
101 bool Print(const DDT::ResourceAcquisition& aFirstSeen, |
|
102 nsACString& out, |
|
103 bool aPrintFirstSeenCx=false) const; |
|
104 |
|
105 /** |
|
106 * mName |
|
107 * A descriptive name for this resource. Used in error |
|
108 * messages etc. |
|
109 */ |
|
110 const char* mName; |
|
111 /** |
|
112 * mType |
|
113 * The more specific type of this resource. Used to implement |
|
114 * special semantics (e.g., reentrancy of monitors). |
|
115 **/ |
|
116 BlockingResourceType mType; |
|
117 /** |
|
118 * mAcquisitionContext |
|
119 * The calling context from which this resource was acquired, or |
|
120 * |CallStack::kNone| if it is currently free (or freed). |
|
121 */ |
|
122 CallStack mAcquisitionContext; |
|
123 }; |
|
124 |
|
125 protected: |
|
126 /** |
|
127 * BlockingResourceBase |
|
128 * Initialize this blocking resource. Also hooks the resource into |
|
129 * instrumentation code. |
|
130 * |
|
131 * Thread safe. |
|
132 * |
|
133 * @param aName A meaningful, unique name that can be used in |
|
134 * error messages, et al. |
|
135 * @param aType The specific type of |this|, if any. |
|
136 **/ |
|
137 BlockingResourceBase(const char* aName, BlockingResourceType aType); |
|
138 |
|
139 ~BlockingResourceBase(); |
|
140 |
|
141 /** |
|
142 * CheckAcquire |
|
143 * |
|
144 * Thread safe. |
|
145 * |
|
146 * @param aCallContext the client's calling context from which the |
|
147 * original acquisition request was made. |
|
148 **/ |
|
149 void CheckAcquire(const CallStack& aCallContext); |
|
150 |
|
151 /** |
|
152 * Acquire |
|
153 * |
|
154 * *NOT* thread safe. Requires ownership of underlying resource. |
|
155 * |
|
156 * @param aCallContext the client's calling context from which the |
|
157 * original acquisition request was made. |
|
158 **/ |
|
159 void Acquire(const CallStack& aCallContext); //NS_NEEDS_RESOURCE(this) |
|
160 |
|
161 /** |
|
162 * Release |
|
163 * Remove this resource from the current thread's acquisition chain. |
|
164 * The resource does not have to be at the front of the chain, although |
|
165 * it is confusing to release resources in a different order than they |
|
166 * are acquired. This generates a warning. |
|
167 * |
|
168 * *NOT* thread safe. Requires ownership of underlying resource. |
|
169 **/ |
|
170 void Release(); //NS_NEEDS_RESOURCE(this) |
|
171 |
|
172 /** |
|
173 * PrintCycle |
|
174 * Append to |out| detailed information about the circular |
|
175 * dependency in |cycle|. Returns true if it *appears* that this |
|
176 * cycle may represent an imminent deadlock, but this is merely a |
|
177 * heuristic; the value returned may be a false positive or false |
|
178 * negative. |
|
179 * |
|
180 * *NOT* thread safe. Calls |Print()|. |
|
181 * |
|
182 * FIXME bug 456272 hack alert: because we can't write call |
|
183 * contexts into strings, all info is written to stderr, but only |
|
184 * some info is written into |out| |
|
185 */ |
|
186 static bool PrintCycle(const DDT::ResourceAcquisitionArray* cycle, |
|
187 nsACString& out); |
|
188 |
|
189 /** |
|
190 * ResourceChainFront |
|
191 * |
|
192 * Thread safe. |
|
193 * |
|
194 * @return the front of the resource acquisition chain, i.e., the last |
|
195 * resource acquired. |
|
196 */ |
|
197 static BlockingResourceBase* ResourceChainFront() |
|
198 { |
|
199 return (BlockingResourceBase*) |
|
200 PR_GetThreadPrivate(sResourceAcqnChainFrontTPI); |
|
201 } |
|
202 |
|
203 /** |
|
204 * ResourceChainPrev |
|
205 * |
|
206 * *NOT* thread safe. Requires ownership of underlying resource. |
|
207 */ |
|
208 static BlockingResourceBase* |
|
209 ResourceChainPrev(const BlockingResourceBase* aResource) |
|
210 { |
|
211 return aResource->mChainPrev; |
|
212 } //NS_NEEDS_RESOURCE(this) |
|
213 |
|
214 /** |
|
215 * ResourceChainAppend |
|
216 * Set |this| to the front of the resource acquisition chain, and link |
|
217 * |this| to |aPrev|. |
|
218 * |
|
219 * *NOT* thread safe. Requires ownership of underlying resource. |
|
220 */ |
|
221 void ResourceChainAppend(BlockingResourceBase* aPrev) |
|
222 { |
|
223 mChainPrev = aPrev; |
|
224 PR_SetThreadPrivate(sResourceAcqnChainFrontTPI, this); |
|
225 } //NS_NEEDS_RESOURCE(this) |
|
226 |
|
227 /** |
|
228 * ResourceChainRemove |
|
229 * Remove |this| from the front of the resource acquisition chain. |
|
230 * |
|
231 * *NOT* thread safe. Requires ownership of underlying resource. |
|
232 */ |
|
233 void ResourceChainRemove() |
|
234 { |
|
235 NS_ASSERTION(this == ResourceChainFront(), "not at chain front"); |
|
236 PR_SetThreadPrivate(sResourceAcqnChainFrontTPI, mChainPrev); |
|
237 } //NS_NEEDS_RESOURCE(this) |
|
238 |
|
239 /** |
|
240 * GetAcquisitionContext |
|
241 * Return the calling context from which this resource was acquired, |
|
242 * or CallStack::kNone if it's currently free. |
|
243 * |
|
244 * *NOT* thread safe. Requires ownership of underlying resource. |
|
245 */ |
|
246 CallStack |
|
247 GetAcquisitionContext() |
|
248 { |
|
249 return mDDEntry->mAcquisitionContext; |
|
250 } |
|
251 |
|
252 /** |
|
253 * SetAcquisitionContext |
|
254 * Set the calling context from which this resource was acquired. |
|
255 * |
|
256 * *NOT* thread safe. Requires ownership of underlying resource. |
|
257 */ |
|
258 void |
|
259 SetAcquisitionContext(CallStack aAcquisitionContext) |
|
260 { |
|
261 mDDEntry->mAcquisitionContext = aAcquisitionContext; |
|
262 } |
|
263 |
|
264 /** |
|
265 * mChainPrev |
|
266 * A series of resource acquisitions creates a chain of orders. This |
|
267 * chain is implemented as a linked list; |mChainPrev| points to the |
|
268 * resource most recently Acquire()'d before this one. |
|
269 **/ |
|
270 BlockingResourceBase* mChainPrev; |
|
271 |
|
272 private: |
|
273 /** |
|
274 * mDDEntry |
|
275 * The key for this BlockingResourceBase in the deadlock detector. |
|
276 */ |
|
277 DeadlockDetectorEntry* mDDEntry; |
|
278 |
|
279 /** |
|
280 * sCallOnce |
|
281 * Ensures static members are initialized only once, and in a |
|
282 * thread-safe way. |
|
283 */ |
|
284 static PRCallOnceType sCallOnce; |
|
285 |
|
286 /** |
|
287 * sResourceAcqnChainFrontTPI |
|
288 * Thread-private index to the front of each thread's resource |
|
289 * acquisition chain. |
|
290 */ |
|
291 static unsigned sResourceAcqnChainFrontTPI; |
|
292 |
|
293 /** |
|
294 * sDeadlockDetector |
|
295 * Does as named. |
|
296 */ |
|
297 static DDT* sDeadlockDetector; |
|
298 |
|
299 /** |
|
300 * InitStatics |
|
301 * Inititialize static members of BlockingResourceBase that can't |
|
302 * be statically initialized. |
|
303 * |
|
304 * *NOT* thread safe. |
|
305 */ |
|
306 static PRStatus InitStatics() { |
|
307 PR_NewThreadPrivateIndex(&sResourceAcqnChainFrontTPI, 0); |
|
308 sDeadlockDetector = new DDT(); |
|
309 if (!sDeadlockDetector) |
|
310 NS_RUNTIMEABORT("can't allocate deadlock detector"); |
|
311 return PR_SUCCESS; |
|
312 } |
|
313 |
|
314 /** |
|
315 * Shutdown |
|
316 * Free static members. |
|
317 * |
|
318 * *NOT* thread safe. |
|
319 */ |
|
320 static void Shutdown() { |
|
321 delete sDeadlockDetector; |
|
322 sDeadlockDetector = 0; |
|
323 } |
|
324 |
|
325 # ifdef MOZILLA_INTERNAL_API |
|
326 // so it can call BlockingResourceBase::Shutdown() |
|
327 friend void LogTerm(); |
|
328 # endif // ifdef MOZILLA_INTERNAL_API |
|
329 |
|
330 #else // non-DEBUG implementation |
|
331 |
|
332 BlockingResourceBase(const char* aName, BlockingResourceType aType) |
|
333 { |
|
334 } |
|
335 |
|
336 ~BlockingResourceBase() |
|
337 { |
|
338 } |
|
339 |
|
340 #endif |
|
341 }; |
|
342 |
|
343 |
|
344 } // namespace mozilla |
|
345 |
|
346 |
|
347 #endif // mozilla_BlockingResourceBase_h |