1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: sw=4 ts=4 et :
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Chris Jones <jones.chris.g@gmail.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "mozilla/BlockingResourceBase.h"
41 :
42 : #ifdef DEBUG
43 : #include "nsAutoPtr.h"
44 :
45 : #include "mozilla/CondVar.h"
46 : #include "mozilla/ReentrantMonitor.h"
47 : #include "mozilla/Mutex.h"
48 : #endif // ifdef DEBUG
49 :
50 : namespace mozilla {
51 : //
52 : // BlockingResourceBase implementation
53 : //
54 :
55 : // static members
56 : const char* const BlockingResourceBase::kResourceTypeName[] =
57 : {
58 : // needs to be kept in sync with BlockingResourceType
59 : "Mutex", "ReentrantMonitor", "CondVar"
60 : };
61 :
62 : #ifdef DEBUG
63 :
64 : PRCallOnceType BlockingResourceBase::sCallOnce;
65 : PRUintn BlockingResourceBase::sResourceAcqnChainFrontTPI = (PRUintn)-1;
66 : BlockingResourceBase::DDT* BlockingResourceBase::sDeadlockDetector;
67 :
68 : bool
69 0 : BlockingResourceBase::DeadlockDetectorEntry::Print(
70 : const DDT::ResourceAcquisition& aFirstSeen,
71 : nsACString& out,
72 : bool aPrintFirstSeenCx) const
73 : {
74 0 : CallStack lastAcquisition = mAcquisitionContext; // RACY, but benign
75 0 : bool maybeCurrentlyAcquired = (CallStack::kNone != lastAcquisition);
76 : CallStack printAcquisition =
77 0 : (aPrintFirstSeenCx || !maybeCurrentlyAcquired) ?
78 0 : aFirstSeen.mCallContext : lastAcquisition;
79 :
80 : fprintf(stderr, "--- %s : %s",
81 0 : kResourceTypeName[mType], mName);
82 0 : out += BlockingResourceBase::kResourceTypeName[mType];
83 0 : out += " : ";
84 0 : out += mName;
85 :
86 0 : if (maybeCurrentlyAcquired) {
87 0 : fputs(" (currently acquired)\n", stderr);
88 0 : out += " (currently acquired)\n";
89 : }
90 :
91 0 : fputs(" calling context\n", stderr);
92 0 : printAcquisition.Print(stderr);
93 :
94 0 : return maybeCurrentlyAcquired;
95 : }
96 :
97 :
98 182900 : BlockingResourceBase::BlockingResourceBase(
99 : const char* aName,
100 : BlockingResourceBase::BlockingResourceType aType)
101 : {
102 : // PR_CallOnce guaranatees that InitStatics is called in a
103 : // thread-safe way
104 182900 : if (PR_SUCCESS != PR_CallOnce(&sCallOnce, InitStatics))
105 0 : NS_RUNTIMEABORT("can't initialize blocking resource static members");
106 :
107 182900 : mDDEntry = new BlockingResourceBase::DeadlockDetectorEntry(aName, aType);
108 182900 : if (!mDDEntry)
109 0 : NS_RUNTIMEABORT("can't allocated deadlock detector entry");
110 :
111 182900 : mChainPrev = 0;
112 182900 : sDeadlockDetector->Add(mDDEntry);
113 182900 : }
114 :
115 :
116 182718 : BlockingResourceBase::~BlockingResourceBase()
117 : {
118 : // we don't check for really obviously bad things like freeing
119 : // Mutexes while they're still locked. it is assumed that the
120 : // base class, or its underlying primitive, will check for such
121 : // stupid mistakes.
122 182718 : mChainPrev = 0; // racy only for stupidly buggy client code
123 182718 : mDDEntry = 0; // owned by deadlock detector
124 182718 : }
125 :
126 :
127 : void
128 24891679 : BlockingResourceBase::CheckAcquire(const CallStack& aCallContext)
129 : {
130 24891679 : if (eCondVar == mDDEntry->mType) {
131 : NS_NOTYETIMPLEMENTED(
132 0 : "FIXME bug 456272: annots. to allow CheckAcquire()ing condvars");
133 0 : return;
134 : }
135 :
136 24891679 : BlockingResourceBase* chainFront = ResourceChainFront();
137 : nsAutoPtr<DDT::ResourceAcquisitionArray> cycle(
138 : sDeadlockDetector->CheckAcquisition(
139 : chainFront ? chainFront->mDDEntry : 0, mDDEntry,
140 49784070 : aCallContext));
141 24892369 : if (!cycle)
142 : return;
143 :
144 0 : fputs("###!!! ERROR: Potential deadlock detected:\n", stderr);
145 0 : nsCAutoString out("Potential deadlock detected:\n");
146 0 : bool maybeImminent = PrintCycle(cycle, out);
147 :
148 0 : if (maybeImminent) {
149 0 : fputs("\n###!!! Deadlock may happen NOW!\n\n", stderr);
150 0 : out.Append("\n###!!! Deadlock may happen NOW!\n\n");
151 : } else {
152 : fputs("\nDeadlock may happen for some other execution\n\n",
153 0 : stderr);
154 0 : out.Append("\nDeadlock may happen for some other execution\n\n");
155 : }
156 :
157 : // XXX can customize behavior on whether we /think/ deadlock is
158 : // XXX about to happen. for example:
159 : // XXX if (maybeImminent)
160 : // NS_RUNTIMEABORT(out.get());
161 0 : NS_ERROR(out.get());
162 : }
163 :
164 :
165 : void
166 24891743 : BlockingResourceBase::Acquire(const CallStack& aCallContext)
167 : {
168 24891743 : if (eCondVar == mDDEntry->mType) {
169 : NS_NOTYETIMPLEMENTED(
170 0 : "FIXME bug 456272: annots. to allow Acquire()ing condvars");
171 0 : return;
172 : }
173 24891743 : NS_ASSERTION(mDDEntry->mAcquisitionContext == CallStack::kNone,
174 : "reacquiring already acquired resource");
175 :
176 24891522 : ResourceChainAppend(ResourceChainFront());
177 24891291 : mDDEntry->mAcquisitionContext = aCallContext;
178 : }
179 :
180 :
181 : void
182 24892007 : BlockingResourceBase::Release()
183 : {
184 24892007 : if (eCondVar == mDDEntry->mType) {
185 : NS_NOTYETIMPLEMENTED(
186 0 : "FIXME bug 456272: annots. to allow Release()ing condvars");
187 0 : return;
188 : }
189 :
190 24892007 : BlockingResourceBase* chainFront = ResourceChainFront();
191 24891876 : NS_ASSERTION(chainFront
192 : && CallStack::kNone != mDDEntry->mAcquisitionContext,
193 : "Release()ing something that hasn't been Acquire()ed");
194 :
195 24891673 : if (chainFront == this) {
196 24891673 : ResourceChainRemove();
197 : }
198 : else {
199 : // not an error, but makes code hard to reason about.
200 0 : NS_WARNING("Resource acquired at calling context\n");
201 0 : mDDEntry->mAcquisitionContext.Print(stderr);
202 0 : NS_WARNING("\nis being released in non-LIFO order; why?");
203 :
204 : // remove this resource from wherever it lives in the chain
205 : // we walk backwards in order of acquisition:
206 : // (1) ...node<-prev<-curr...
207 : // / /
208 : // (2) ...prev<-curr...
209 0 : BlockingResourceBase* curr = chainFront;
210 0 : BlockingResourceBase* prev = nsnull;
211 0 : while (curr && (prev = curr->mChainPrev) && (prev != this))
212 0 : curr = prev;
213 0 : if (prev == this)
214 0 : curr->mChainPrev = prev->mChainPrev;
215 : }
216 :
217 24891208 : mDDEntry->mAcquisitionContext = CallStack::kNone;
218 : }
219 :
220 :
221 : bool
222 0 : BlockingResourceBase::PrintCycle(const DDT::ResourceAcquisitionArray* aCycle,
223 : nsACString& out)
224 : {
225 0 : NS_ASSERTION(aCycle->Length() > 1, "need > 1 element for cycle!");
226 :
227 0 : bool maybeImminent = true;
228 :
229 0 : fputs("=== Cyclical dependency starts at\n", stderr);
230 0 : out += "Cyclical dependency starts at\n";
231 :
232 0 : const DDT::ResourceAcquisition res = aCycle->ElementAt(0);
233 0 : maybeImminent &= res.mResource->Print(res, out);
234 :
235 : DDT::ResourceAcquisitionArray::index_type i;
236 0 : DDT::ResourceAcquisitionArray::size_type len = aCycle->Length();
237 0 : const DDT::ResourceAcquisition* it = 1 + aCycle->Elements();
238 0 : for (i = 1; i < len - 1; ++i, ++it) {
239 0 : fputs("\n--- Next dependency:\n", stderr);
240 0 : out += "\nNext dependency:\n";
241 :
242 0 : maybeImminent &= it->mResource->Print(*it, out);
243 : }
244 :
245 0 : fputs("\n=== Cycle completed at\n", stderr);
246 0 : out += "Cycle completed at\n";
247 0 : it->mResource->Print(*it, out, true);
248 :
249 0 : return maybeImminent;
250 : }
251 :
252 :
253 : //
254 : // Debug implementation of Mutex
255 : void
256 3400873 : Mutex::Lock()
257 : {
258 3400873 : CallStack callContext = CallStack();
259 :
260 3400880 : CheckAcquire(callContext);
261 3400901 : PR_Lock(mLock);
262 3400899 : Acquire(callContext); // protected by mLock
263 3400882 : }
264 :
265 : void
266 3400874 : Mutex::Unlock()
267 : {
268 3400874 : Release(); // protected by mLock
269 3400843 : PRStatus status = PR_Unlock(mLock);
270 3400867 : NS_ASSERTION(PR_SUCCESS == status, "bad Mutex::Unlock()");
271 3400867 : }
272 :
273 :
274 : //
275 : // Debug implementation of ReentrantMonitor
276 : void
277 21773368 : ReentrantMonitor::Enter()
278 : {
279 21773368 : BlockingResourceBase* chainFront = ResourceChainFront();
280 :
281 : // the code below implements monitor reentrancy semantics
282 :
283 21773140 : if (this == chainFront) {
284 : // immediately re-entered the monitor: acceptable
285 447820 : PR_EnterMonitor(mReentrantMonitor);
286 447820 : ++mEntryCount;
287 447820 : return;
288 : }
289 :
290 21325320 : CallStack callContext = CallStack();
291 :
292 : // this is sort of a hack around not recording the thread that
293 : // owns this monitor
294 21325288 : if (chainFront) {
295 499782 : for (BlockingResourceBase* br = ResourceChainPrev(chainFront);
296 : br;
297 : br = ResourceChainPrev(br)) {
298 42585 : if (br == this) {
299 : NS_WARNING(
300 : "Re-entering ReentrantMonitor after acquiring other resources.\n"
301 0 : "At calling context\n");
302 0 : GetAcquisitionContext().Print(stderr);
303 :
304 : // show the caller why this is potentially bad
305 0 : CheckAcquire(callContext);
306 :
307 0 : PR_EnterMonitor(mReentrantMonitor);
308 0 : ++mEntryCount;
309 0 : return;
310 : }
311 : }
312 : }
313 :
314 21325288 : CheckAcquire(callContext);
315 21325623 : PR_EnterMonitor(mReentrantMonitor);
316 21325612 : NS_ASSERTION(0 == mEntryCount, "ReentrantMonitor isn't free!");
317 21325612 : Acquire(callContext); // protected by mReentrantMonitor
318 21325382 : mEntryCount = 1;
319 : }
320 :
321 : void
322 21773328 : ReentrantMonitor::Exit()
323 : {
324 21773328 : if (0 == --mEntryCount)
325 21325519 : Release(); // protected by mReentrantMonitor
326 21773130 : PRStatus status = PR_ExitMonitor(mReentrantMonitor);
327 21773221 : NS_ASSERTION(PR_SUCCESS == status, "bad ReentrantMonitor::Exit()");
328 21773221 : }
329 :
330 : nsresult
331 68033 : ReentrantMonitor::Wait(PRIntervalTime interval)
332 : {
333 68033 : AssertCurrentThreadIn();
334 :
335 : // save monitor state and reset it to empty
336 68033 : PRInt32 savedEntryCount = mEntryCount;
337 68033 : CallStack savedAcquisitionContext = GetAcquisitionContext();
338 68033 : BlockingResourceBase* savedChainPrev = mChainPrev;
339 68033 : mEntryCount = 0;
340 68033 : SetAcquisitionContext(CallStack::kNone);
341 68033 : mChainPrev = 0;
342 :
343 : // give up the monitor until we're back from Wait()
344 : nsresult rv =
345 68033 : PR_Wait(mReentrantMonitor, interval) == PR_SUCCESS ?
346 68033 : NS_OK : NS_ERROR_FAILURE;
347 :
348 : // restore saved state
349 68033 : mEntryCount = savedEntryCount;
350 68033 : SetAcquisitionContext(savedAcquisitionContext);
351 68033 : mChainPrev = savedChainPrev;
352 :
353 68033 : return rv;
354 : }
355 :
356 :
357 : //
358 : // Debug implementation of CondVar
359 : nsresult
360 43838 : CondVar::Wait(PRIntervalTime interval)
361 : {
362 43838 : AssertCurrentThreadOwnsMutex();
363 :
364 : // save mutex state and reset to empty
365 43838 : CallStack savedAcquisitionContext = mLock->GetAcquisitionContext();
366 43838 : BlockingResourceBase* savedChainPrev = mLock->mChainPrev;
367 43838 : mLock->SetAcquisitionContext(CallStack::kNone);
368 43838 : mLock->mChainPrev = 0;
369 :
370 : // give up mutex until we're back from Wait()
371 : nsresult rv =
372 43838 : PR_WaitCondVar(mCvar, interval) == PR_SUCCESS ?
373 43838 : NS_OK : NS_ERROR_FAILURE;
374 :
375 : // restore saved state
376 43838 : mLock->SetAcquisitionContext(savedAcquisitionContext);
377 43838 : mLock->mChainPrev = savedChainPrev;
378 :
379 43838 : return rv;
380 : }
381 :
382 : #endif // ifdef DEBUG
383 :
384 :
385 : } // namespace mozilla
|