xpcom/glue/BlockingResourceBase.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:a4fc5f3cdc16
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 #include "mozilla/BlockingResourceBase.h"
8
9 #ifdef DEBUG
10 #include "nsAutoPtr.h"
11
12 #include "mozilla/CondVar.h"
13 #include "mozilla/ReentrantMonitor.h"
14 #include "mozilla/Mutex.h"
15
16 #ifdef MOZILLA_INTERNAL_API
17 #include "GeckoProfiler.h"
18 #endif //MOZILLA_INTERNAL_API
19
20 #endif // ifdef DEBUG
21
22 namespace mozilla {
23 //
24 // BlockingResourceBase implementation
25 //
26
27 // static members
28 const char* const BlockingResourceBase::kResourceTypeName[] =
29 {
30 // needs to be kept in sync with BlockingResourceType
31 "Mutex", "ReentrantMonitor", "CondVar"
32 };
33
34 #ifdef DEBUG
35
36 PRCallOnceType BlockingResourceBase::sCallOnce;
37 unsigned BlockingResourceBase::sResourceAcqnChainFrontTPI = (unsigned)-1;
38 BlockingResourceBase::DDT* BlockingResourceBase::sDeadlockDetector;
39
40 bool
41 BlockingResourceBase::DeadlockDetectorEntry::Print(
42 const DDT::ResourceAcquisition& aFirstSeen,
43 nsACString& out,
44 bool aPrintFirstSeenCx) const
45 {
46 CallStack lastAcquisition = mAcquisitionContext; // RACY, but benign
47 bool maybeCurrentlyAcquired = (CallStack::kNone != lastAcquisition);
48 CallStack printAcquisition =
49 (aPrintFirstSeenCx || !maybeCurrentlyAcquired) ?
50 aFirstSeen.mCallContext : lastAcquisition;
51
52 fprintf(stderr, "--- %s : %s",
53 kResourceTypeName[mType], mName);
54 out += BlockingResourceBase::kResourceTypeName[mType];
55 out += " : ";
56 out += mName;
57
58 if (maybeCurrentlyAcquired) {
59 fputs(" (currently acquired)\n", stderr);
60 out += " (currently acquired)\n";
61 }
62
63 fputs(" calling context\n", stderr);
64 printAcquisition.Print(stderr);
65
66 return maybeCurrentlyAcquired;
67 }
68
69
70 BlockingResourceBase::BlockingResourceBase(
71 const char* aName,
72 BlockingResourceBase::BlockingResourceType aType)
73 {
74 // PR_CallOnce guaranatees that InitStatics is called in a
75 // thread-safe way
76 if (PR_SUCCESS != PR_CallOnce(&sCallOnce, InitStatics))
77 NS_RUNTIMEABORT("can't initialize blocking resource static members");
78
79 mDDEntry = new BlockingResourceBase::DeadlockDetectorEntry(aName, aType);
80 mChainPrev = 0;
81 sDeadlockDetector->Add(mDDEntry);
82 }
83
84
85 BlockingResourceBase::~BlockingResourceBase()
86 {
87 // we don't check for really obviously bad things like freeing
88 // Mutexes while they're still locked. it is assumed that the
89 // base class, or its underlying primitive, will check for such
90 // stupid mistakes.
91 mChainPrev = 0; // racy only for stupidly buggy client code
92 mDDEntry = 0; // owned by deadlock detector
93 }
94
95
96 void
97 BlockingResourceBase::CheckAcquire(const CallStack& aCallContext)
98 {
99 if (eCondVar == mDDEntry->mType) {
100 NS_NOTYETIMPLEMENTED(
101 "FIXME bug 456272: annots. to allow CheckAcquire()ing condvars");
102 return;
103 }
104
105 BlockingResourceBase* chainFront = ResourceChainFront();
106 nsAutoPtr<DDT::ResourceAcquisitionArray> cycle(
107 sDeadlockDetector->CheckAcquisition(
108 chainFront ? chainFront->mDDEntry : 0, mDDEntry,
109 aCallContext));
110 if (!cycle)
111 return;
112
113 fputs("###!!! ERROR: Potential deadlock detected:\n", stderr);
114 nsAutoCString out("Potential deadlock detected:\n");
115 bool maybeImminent = PrintCycle(cycle, out);
116
117 if (maybeImminent) {
118 fputs("\n###!!! Deadlock may happen NOW!\n\n", stderr);
119 out.Append("\n###!!! Deadlock may happen NOW!\n\n");
120 } else {
121 fputs("\nDeadlock may happen for some other execution\n\n",
122 stderr);
123 out.Append("\nDeadlock may happen for some other execution\n\n");
124 }
125
126 // XXX can customize behavior on whether we /think/ deadlock is
127 // XXX about to happen. for example:
128 // XXX if (maybeImminent)
129 // NS_RUNTIMEABORT(out.get());
130 NS_ERROR(out.get());
131 }
132
133
134 void
135 BlockingResourceBase::Acquire(const CallStack& aCallContext)
136 {
137 if (eCondVar == mDDEntry->mType) {
138 NS_NOTYETIMPLEMENTED(
139 "FIXME bug 456272: annots. to allow Acquire()ing condvars");
140 return;
141 }
142 NS_ASSERTION(mDDEntry->mAcquisitionContext == CallStack::kNone,
143 "reacquiring already acquired resource");
144
145 ResourceChainAppend(ResourceChainFront());
146 mDDEntry->mAcquisitionContext = aCallContext;
147 }
148
149
150 void
151 BlockingResourceBase::Release()
152 {
153 if (eCondVar == mDDEntry->mType) {
154 NS_NOTYETIMPLEMENTED(
155 "FIXME bug 456272: annots. to allow Release()ing condvars");
156 return;
157 }
158
159 BlockingResourceBase* chainFront = ResourceChainFront();
160 NS_ASSERTION(chainFront
161 && CallStack::kNone != mDDEntry->mAcquisitionContext,
162 "Release()ing something that hasn't been Acquire()ed");
163
164 if (chainFront == this) {
165 ResourceChainRemove();
166 }
167 else {
168 // not an error, but makes code hard to reason about.
169 NS_WARNING("Resource acquired at calling context\n");
170 mDDEntry->mAcquisitionContext.Print(stderr);
171 NS_WARNING("\nis being released in non-LIFO order; why?");
172
173 // remove this resource from wherever it lives in the chain
174 // we walk backwards in order of acquisition:
175 // (1) ...node<-prev<-curr...
176 // / /
177 // (2) ...prev<-curr...
178 BlockingResourceBase* curr = chainFront;
179 BlockingResourceBase* prev = nullptr;
180 while (curr && (prev = curr->mChainPrev) && (prev != this))
181 curr = prev;
182 if (prev == this)
183 curr->mChainPrev = prev->mChainPrev;
184 }
185
186 mDDEntry->mAcquisitionContext = CallStack::kNone;
187 }
188
189
190 bool
191 BlockingResourceBase::PrintCycle(const DDT::ResourceAcquisitionArray* aCycle,
192 nsACString& out)
193 {
194 NS_ASSERTION(aCycle->Length() > 1, "need > 1 element for cycle!");
195
196 bool maybeImminent = true;
197
198 fputs("=== Cyclical dependency starts at\n", stderr);
199 out += "Cyclical dependency starts at\n";
200
201 const DDT::ResourceAcquisition res = aCycle->ElementAt(0);
202 maybeImminent &= res.mResource->Print(res, out);
203
204 DDT::ResourceAcquisitionArray::index_type i;
205 DDT::ResourceAcquisitionArray::size_type len = aCycle->Length();
206 const DDT::ResourceAcquisition* it = 1 + aCycle->Elements();
207 for (i = 1; i < len - 1; ++i, ++it) {
208 fputs("\n--- Next dependency:\n", stderr);
209 out += "\nNext dependency:\n";
210
211 maybeImminent &= it->mResource->Print(*it, out);
212 }
213
214 fputs("\n=== Cycle completed at\n", stderr);
215 out += "Cycle completed at\n";
216 it->mResource->Print(*it, out, true);
217
218 return maybeImminent;
219 }
220
221
222 //
223 // Debug implementation of (OffTheBooks)Mutex
224 void
225 OffTheBooksMutex::Lock()
226 {
227 CallStack callContext = CallStack();
228
229 CheckAcquire(callContext);
230 PR_Lock(mLock);
231 Acquire(callContext); // protected by mLock
232 }
233
234 void
235 OffTheBooksMutex::Unlock()
236 {
237 Release(); // protected by mLock
238 PRStatus status = PR_Unlock(mLock);
239 NS_ASSERTION(PR_SUCCESS == status, "bad Mutex::Unlock()");
240 }
241
242
243 //
244 // Debug implementation of ReentrantMonitor
245 void
246 ReentrantMonitor::Enter()
247 {
248 BlockingResourceBase* chainFront = ResourceChainFront();
249
250 // the code below implements monitor reentrancy semantics
251
252 if (this == chainFront) {
253 // immediately re-entered the monitor: acceptable
254 PR_EnterMonitor(mReentrantMonitor);
255 ++mEntryCount;
256 return;
257 }
258
259 CallStack callContext = CallStack();
260
261 // this is sort of a hack around not recording the thread that
262 // owns this monitor
263 if (chainFront) {
264 for (BlockingResourceBase* br = ResourceChainPrev(chainFront);
265 br;
266 br = ResourceChainPrev(br)) {
267 if (br == this) {
268 NS_WARNING(
269 "Re-entering ReentrantMonitor after acquiring other resources.\n"
270 "At calling context\n");
271 GetAcquisitionContext().Print(stderr);
272
273 // show the caller why this is potentially bad
274 CheckAcquire(callContext);
275
276 PR_EnterMonitor(mReentrantMonitor);
277 ++mEntryCount;
278 return;
279 }
280 }
281 }
282
283 CheckAcquire(callContext);
284 PR_EnterMonitor(mReentrantMonitor);
285 NS_ASSERTION(0 == mEntryCount, "ReentrantMonitor isn't free!");
286 Acquire(callContext); // protected by mReentrantMonitor
287 mEntryCount = 1;
288 }
289
290 void
291 ReentrantMonitor::Exit()
292 {
293 if (0 == --mEntryCount)
294 Release(); // protected by mReentrantMonitor
295 PRStatus status = PR_ExitMonitor(mReentrantMonitor);
296 NS_ASSERTION(PR_SUCCESS == status, "bad ReentrantMonitor::Exit()");
297 }
298
299 nsresult
300 ReentrantMonitor::Wait(PRIntervalTime interval)
301 {
302 AssertCurrentThreadIn();
303
304 // save monitor state and reset it to empty
305 int32_t savedEntryCount = mEntryCount;
306 CallStack savedAcquisitionContext = GetAcquisitionContext();
307 BlockingResourceBase* savedChainPrev = mChainPrev;
308 mEntryCount = 0;
309 SetAcquisitionContext(CallStack::kNone);
310 mChainPrev = 0;
311
312 nsresult rv;
313 #ifdef MOZILLA_INTERNAL_API
314 {
315 GeckoProfilerSleepRAII profiler_sleep;
316 #endif //MOZILLA_INTERNAL_API
317
318 // give up the monitor until we're back from Wait()
319 rv = PR_Wait(mReentrantMonitor, interval) == PR_SUCCESS ?
320 NS_OK : NS_ERROR_FAILURE;
321
322 #ifdef MOZILLA_INTERNAL_API
323 }
324 #endif //MOZILLA_INTERNAL_API
325
326 // restore saved state
327 mEntryCount = savedEntryCount;
328 SetAcquisitionContext(savedAcquisitionContext);
329 mChainPrev = savedChainPrev;
330
331 return rv;
332 }
333
334
335 //
336 // Debug implementation of CondVar
337 nsresult
338 CondVar::Wait(PRIntervalTime interval)
339 {
340 AssertCurrentThreadOwnsMutex();
341
342 // save mutex state and reset to empty
343 CallStack savedAcquisitionContext = mLock->GetAcquisitionContext();
344 BlockingResourceBase* savedChainPrev = mLock->mChainPrev;
345 mLock->SetAcquisitionContext(CallStack::kNone);
346 mLock->mChainPrev = 0;
347
348 // give up mutex until we're back from Wait()
349 nsresult rv =
350 PR_WaitCondVar(mCvar, interval) == PR_SUCCESS ?
351 NS_OK : NS_ERROR_FAILURE;
352
353 // restore saved state
354 mLock->SetAcquisitionContext(savedAcquisitionContext);
355 mLock->mChainPrev = savedChainPrev;
356
357 return rv;
358 }
359
360 #endif // ifdef DEBUG
361
362
363 } // namespace mozilla

mercurial