VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/threads/nsAutoLock.cpp@ 95259

Last change on this file since 95259 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.8 KB
Line 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38#include "nsAutoLock.h"
39
40#ifdef DEBUG
41
42#include "plhash.h"
43#include "prprf.h"
44#include "prlock.h"
45#include "prthread.h"
46#include "nsDebug.h"
47#include "nsVoidArray.h"
48
49#ifdef NS_TRACE_MALLOC_XXX
50# include <stdio.h>
51# include "nsTraceMalloc.h"
52#endif
53
54static PRUintn LockStackTPI = (PRUintn)-1;
55static PLHashTable* OrderTable = 0;
56static PRLock* OrderTableLock = 0;
57
58static const char* const LockTypeNames[] = {"Lock", "Monitor", "CMonitor"};
59
60struct nsNamedVector : public nsVoidArray {
61 const char* mName;
62
63#ifdef NS_TRACE_MALLOC_XXX
64 // Callsites for the inner locks/monitors stored in our base nsVoidArray.
65 // This array parallels our base nsVoidArray.
66 nsVoidArray mInnerSites;
67#endif
68
69 nsNamedVector(const char* name = 0, PRUint32 initialSize = 0)
70 : nsVoidArray(initialSize),
71 mName(name)
72 {
73 }
74};
75
76static void * PR_CALLBACK
77_hash_alloc_table(void *pool, PRSize size)
78{
79 return operator new(size);
80}
81
82static void PR_CALLBACK
83_hash_free_table(void *pool, void *item)
84{
85 operator delete(item);
86}
87
88static PLHashEntry * PR_CALLBACK
89_hash_alloc_entry(void *pool, const void *key)
90{
91 return new PLHashEntry;
92}
93
94/*
95 * Because monitors and locks may be associated with an nsAutoLockBase,
96 * without having had their associated nsNamedVector created explicitly in
97 * nsAutoMonitor::NewMonitor/DeleteMonitor, we need to provide a freeEntry
98 * PLHashTable hook, to avoid leaking nsNamedVectors which are replaced by
99 * nsAutoMonitor::NewMonitor.
100 *
101 * There is still a problem with the OrderTable containing orphaned
102 * nsNamedVector entries, for manually created locks wrapped by nsAutoLocks.
103 * (there should be no manually created monitors wrapped by nsAutoMonitors:
104 * you should use nsAutoMonitor::NewMonitor and nsAutoMonitor::DestroyMonitor
105 * instead of PR_NewMonitor and PR_DestroyMonitor). These lock vectors don't
106 * strictly leak, as they are killed on shutdown, but there are unnecessary
107 * named vectors in the hash table that outlive their associated locks.
108 *
109 * XXX so we should have nsLock, nsMonitor, etc. and strongly type their
110 * XXX nsAutoXXX counterparts to take only the non-auto types as inputs
111 */
112static void PR_CALLBACK
113_hash_free_entry(void *pool, PLHashEntry *entry, PRUintn flag)
114{
115 nsNamedVector* vec = (nsNamedVector*) entry->value;
116 if (vec) {
117 entry->value = 0;
118 delete vec;
119 }
120 if (flag == HT_FREE_ENTRY)
121 delete entry;
122}
123
124static const PLHashAllocOps _hash_alloc_ops = {
125 _hash_alloc_table, _hash_free_table,
126 _hash_alloc_entry, _hash_free_entry
127};
128
129PR_STATIC_CALLBACK(PRIntn)
130_purge_one(PLHashEntry* he, PRIntn cnt, void* arg)
131{
132 nsNamedVector* vec = (nsNamedVector*) he->value;
133
134 if (he->key == arg)
135 return HT_ENUMERATE_REMOVE;
136 vec->RemoveElement(arg);
137 return HT_ENUMERATE_NEXT;
138}
139
140PR_STATIC_CALLBACK(void)
141OnMonitorRecycle(void* addr)
142{
143 PR_Lock(OrderTableLock);
144 PL_HashTableEnumerateEntries(OrderTable, _purge_one, addr);
145 PR_Unlock(OrderTableLock);
146}
147
148PR_STATIC_CALLBACK(PLHashNumber)
149_hash_pointer(const void* key)
150{
151 return PLHashNumber(NS_PTR_TO_INT32(key)) >> 2;
152}
153
154// Must be single-threaded here, early in primordial thread.
155static void InitAutoLockStatics()
156{
157 (void) PR_NewThreadPrivateIndex(&LockStackTPI, 0);
158 OrderTable = PL_NewHashTable(64, _hash_pointer,
159 PL_CompareValues, PL_CompareValues,
160 &_hash_alloc_ops, 0);
161 if (OrderTable && !(OrderTableLock = PR_NewLock())) {
162 PL_HashTableDestroy(OrderTable);
163 OrderTable = 0;
164 }
165 PR_CSetOnMonitorRecycle(OnMonitorRecycle);
166}
167
168void _FreeAutoLockStatics()
169{
170 PLHashTable* table = OrderTable;
171 if (!table) return;
172
173 // Called at shutdown, so we don't need to lock.
174 PR_CSetOnMonitorRecycle(0);
175 PR_DestroyLock(OrderTableLock);
176 OrderTableLock = 0;
177 PL_HashTableDestroy(table);
178 OrderTable = 0;
179}
180
181static nsNamedVector* GetVector(PLHashTable* table, const void* key)
182{
183 PLHashNumber hash = _hash_pointer(key);
184 PLHashEntry** hep = PL_HashTableRawLookup(table, hash, key);
185 PLHashEntry* he = *hep;
186 if (he)
187 return (nsNamedVector*) he->value;
188 nsNamedVector* vec = new nsNamedVector();
189 if (vec)
190 PL_HashTableRawAdd(table, hep, hash, key, vec);
191 return vec;
192}
193
194// We maintain an acyclic graph in OrderTable, so recursion can't diverge.
195static PRBool Reachable(PLHashTable* table, const void* goal, const void* start)
196{
197 PR_ASSERT(goal);
198 PR_ASSERT(start);
199 nsNamedVector* vec = GetVector(table, start);
200 for (PRUint32 i = 0, n = vec->Count(); i < n; i++) {
201 void* addr = vec->ElementAt(i);
202 if (addr == goal || Reachable(table, goal, addr))
203 return PR_TRUE;
204 }
205 return PR_FALSE;
206}
207
208static PRBool WellOrdered(const void* addr1, const void* addr2,
209 const void *callsite2, PRUint32* index2p,
210 nsNamedVector** vec1p, nsNamedVector** vec2p)
211{
212 PRBool rv = PR_TRUE;
213 PLHashTable* table = OrderTable;
214 if (!table) return rv;
215 PR_Lock(OrderTableLock);
216
217 // Check whether we've already asserted (addr1 < addr2).
218 nsNamedVector* vec1 = GetVector(table, addr1);
219 if (vec1) {
220 PRUint32 i, n;
221
222 for (i = 0, n = vec1->Count(); i < n; i++)
223 if (vec1->ElementAt(i) == addr2)
224 break;
225
226 if (i == n) {
227 // Now check for (addr2 < addr1) and return false if so.
228 nsNamedVector* vec2 = GetVector(table, addr2);
229 if (vec2) {
230 for (i = 0, n = vec2->Count(); i < n; i++) {
231 void* addri = vec2->ElementAt(i);
232 PR_ASSERT(addri);
233 if (addri == addr1 || Reachable(table, addr1, addri)) {
234 *index2p = i;
235 *vec1p = vec1;
236 *vec2p = vec2;
237 rv = PR_FALSE;
238 break;
239 }
240 }
241
242 if (rv) {
243 // Assert (addr1 < addr2) into the order table.
244 // XXX fix plvector/nsVector to use const void*
245 vec1->AppendElement((void*) addr2);
246#ifdef NS_TRACE_MALLOC_XXX
247 vec1->mInnerSites.AppendElement((void*) callsite2);
248#endif
249 }
250 }
251 }
252 }
253
254 PR_Unlock(OrderTableLock);
255 return rv;
256}
257
258nsAutoLockBase::nsAutoLockBase(void* addr, nsAutoLockType type)
259{
260 if (LockStackTPI == PRUintn(-1))
261 InitAutoLockStatics();
262
263 nsAutoLockBase* stackTop =
264 (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI);
265 if (stackTop) {
266 if (stackTop->mAddr == addr) {
267 // Ignore reentry: it's legal for monitors, and NSPR will assert
268 // if you reenter a PRLock.
269 } else if (!addr) {
270 // Ignore null addresses: the caller promises not to use the
271 // lock at all, and NSPR will assert if you enter it.
272 } else {
273 const void* node =
274#ifdef NS_TRACE_MALLOC_XXX
275 NS_GetStackTrace(1)
276#else
277 nsnull
278#endif
279 ;
280 nsNamedVector* vec1;
281 nsNamedVector* vec2;
282 PRUint32 i2;
283
284 if (!WellOrdered(stackTop->mAddr, addr, node, &i2, &vec1, &vec2)) {
285 char buf[128];
286 PR_snprintf(buf, sizeof buf,
287 "Potential deadlock between %s%s@%p and %s%s@%p",
288 vec1->mName ? vec1->mName : "",
289 LockTypeNames[stackTop->mType],
290 stackTop->mAddr,
291 vec2->mName ? vec2->mName : "",
292 LockTypeNames[type],
293 addr);
294#ifdef NS_TRACE_MALLOC_XXX
295 fprintf(stderr, "\n*** %s\n\nCurrent stack:\n", buf);
296 NS_DumpStackTrace(node, stderr);
297
298 fputs("\nPrevious stack:\n", stderr);
299 NS_DumpStackTrace(vec2->mInnerSites.ElementAt(i2), stderr);
300 putc('\n', stderr);
301#endif
302 NS_ERROR(buf);
303 }
304 }
305 }
306
307 mAddr = addr;
308 mDown = stackTop;
309 mType = type;
310 if (mAddr)
311 (void) PR_SetThreadPrivate(LockStackTPI, this);
312}
313
314nsAutoLockBase::~nsAutoLockBase()
315{
316 if (mAddr)
317 (void) PR_SetThreadPrivate(LockStackTPI, mDown);
318}
319
320void nsAutoLockBase::Show()
321{
322 if (!mAddr)
323 return;
324 nsAutoLockBase* curr = (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI);
325 nsAutoLockBase* prev = nsnull;
326 while (curr != mDown) {
327 prev = curr;
328 curr = prev->mDown;
329 }
330 if (!prev)
331 PR_SetThreadPrivate(LockStackTPI, this);
332 else
333 prev->mDown = this;
334}
335
336void nsAutoLockBase::Hide()
337{
338 if (!mAddr)
339 return;
340 nsAutoLockBase* curr = (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI);
341 nsAutoLockBase* prev = nsnull;
342 while (curr != this) {
343 prev = curr;
344 curr = prev->mDown;
345 }
346 if (!prev)
347 PR_SetThreadPrivate(LockStackTPI, mDown);
348 else
349 prev->mDown = mDown;
350}
351
352#endif /* DEBUG */
353
354PRMonitor* nsAutoMonitor::NewMonitor(const char* name)
355{
356 PRMonitor* mon = PR_NewMonitor();
357#ifdef DEBUG
358 if (mon && OrderTable) {
359 nsNamedVector* value = new nsNamedVector(name);
360 if (value) {
361 PR_Lock(OrderTableLock);
362 PL_HashTableAdd(OrderTable, mon, value);
363 PR_Unlock(OrderTableLock);
364 }
365 }
366#endif
367 return mon;
368}
369
370void nsAutoMonitor::DestroyMonitor(PRMonitor* mon)
371{
372#ifdef DEBUG
373 if (OrderTable)
374 OnMonitorRecycle(mon);
375#endif
376 PR_DestroyMonitor(mon);
377}
378
379void nsAutoMonitor::Enter()
380{
381#ifdef DEBUG
382 if (!mAddr) {
383 NS_ERROR("It is not legal to enter a null monitor");
384 return;
385 }
386 nsAutoLockBase* stackTop =
387 (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI);
388 NS_ASSERTION(stackTop == mDown, "non-LIFO nsAutoMonitor::Enter");
389 mDown = stackTop;
390 (void) PR_SetThreadPrivate(LockStackTPI, this);
391#endif
392 PR_EnterMonitor(mMonitor);
393 mLockCount += 1;
394}
395
396void nsAutoMonitor::Exit()
397{
398#ifdef DEBUG
399 if (!mAddr) {
400 NS_ERROR("It is not legal to exit a null monitor");
401 return;
402 }
403 (void) PR_SetThreadPrivate(LockStackTPI, mDown);
404#endif
405 PRStatus status = PR_ExitMonitor(mMonitor);
406 NS_ASSERTION(status == PR_SUCCESS, "PR_ExitMonitor failed");
407 mLockCount -= 1;
408}
409
410// XXX we don't worry about cached monitors being destroyed behind our back.
411// XXX current NSPR (mozilla/nsprpub/pr/src/threads/prcmon.c) never destroys
412// XXX a cached monitor! potential resource pig in conjunction with necko...
413
414void nsAutoCMonitor::Enter()
415{
416#ifdef DEBUG
417 nsAutoLockBase* stackTop =
418 (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI);
419 NS_ASSERTION(stackTop == mDown, "non-LIFO nsAutoCMonitor::Enter");
420 mDown = stackTop;
421 (void) PR_SetThreadPrivate(LockStackTPI, this);
422#endif
423 PR_CEnterMonitor(mLockObject);
424 mLockCount += 1;
425}
426
427void nsAutoCMonitor::Exit()
428{
429#ifdef DEBUG
430 (void) PR_SetThreadPrivate(LockStackTPI, mDown);
431#endif
432 PRStatus status = PR_CExitMonitor(mLockObject);
433 NS_ASSERTION(status == PR_SUCCESS, "PR_CExitMonitor failed");
434 mLockCount -= 1;
435}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette