VirtualBox

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

Last change on this file since 101905 was 101905, checked in by vboxsync, 13 months ago

libs/xpcom: Remove unused code from nsprpub, bugref:10545

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.9 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}
166
167void _FreeAutoLockStatics()
168{
169 PLHashTable* table = OrderTable;
170 if (!table) return;
171
172 // Called at shutdown, so we don't need to lock.
173 PR_DestroyLock(OrderTableLock);
174 OrderTableLock = 0;
175 PL_HashTableDestroy(table);
176 OrderTable = 0;
177}
178
179static nsNamedVector* GetVector(PLHashTable* table, const void* key)
180{
181 PLHashNumber hash = _hash_pointer(key);
182 PLHashEntry** hep = PL_HashTableRawLookup(table, hash, key);
183 PLHashEntry* he = *hep;
184 if (he)
185 return (nsNamedVector*) he->value;
186 nsNamedVector* vec = new nsNamedVector();
187 if (vec)
188 PL_HashTableRawAdd(table, hep, hash, key, vec);
189 return vec;
190}
191
192// We maintain an acyclic graph in OrderTable, so recursion can't diverge.
193static PRBool Reachable(PLHashTable* table, const void* goal, const void* start)
194{
195 PR_ASSERT(goal);
196 PR_ASSERT(start);
197 nsNamedVector* vec = GetVector(table, start);
198 for (PRUint32 i = 0, n = vec->Count(); i < n; i++) {
199 void* addr = vec->ElementAt(i);
200 if (addr == goal || Reachable(table, goal, addr))
201 return PR_TRUE;
202 }
203 return PR_FALSE;
204}
205
206static PRBool WellOrdered(const void* addr1, const void* addr2,
207 const void *callsite2, PRUint32* index2p,
208 nsNamedVector** vec1p, nsNamedVector** vec2p)
209{
210 PRBool rv = PR_TRUE;
211 PLHashTable* table = OrderTable;
212 if (!table) return rv;
213 PR_Lock(OrderTableLock);
214
215 // Check whether we've already asserted (addr1 < addr2).
216 nsNamedVector* vec1 = GetVector(table, addr1);
217 if (vec1) {
218 PRUint32 i, n;
219
220 for (i = 0, n = vec1->Count(); i < n; i++)
221 if (vec1->ElementAt(i) == addr2)
222 break;
223
224 if (i == n) {
225 // Now check for (addr2 < addr1) and return false if so.
226 nsNamedVector* vec2 = GetVector(table, addr2);
227 if (vec2) {
228 for (i = 0, n = vec2->Count(); i < n; i++) {
229 void* addri = vec2->ElementAt(i);
230 PR_ASSERT(addri);
231 if (addri == addr1 || Reachable(table, addr1, addri)) {
232 *index2p = i;
233 *vec1p = vec1;
234 *vec2p = vec2;
235 rv = PR_FALSE;
236 break;
237 }
238 }
239
240 if (rv) {
241 // Assert (addr1 < addr2) into the order table.
242 // XXX fix plvector/nsVector to use const void*
243 vec1->AppendElement((void*) addr2);
244#ifdef NS_TRACE_MALLOC_XXX
245 vec1->mInnerSites.AppendElement((void*) callsite2);
246#endif
247 }
248 }
249 }
250 }
251
252 PR_Unlock(OrderTableLock);
253 return rv;
254}
255
256nsAutoLockBase::nsAutoLockBase(void* addr, nsAutoLockType type)
257{
258 if (LockStackTPI == PRUintn(-1))
259 InitAutoLockStatics();
260
261 nsAutoLockBase* stackTop =
262 (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI);
263 if (stackTop) {
264 if (stackTop->mAddr == addr) {
265 // Ignore reentry: it's legal for monitors, and NSPR will assert
266 // if you reenter a PRLock.
267 } else if (!addr) {
268 // Ignore null addresses: the caller promises not to use the
269 // lock at all, and NSPR will assert if you enter it.
270 } else {
271 const void* node =
272#ifdef NS_TRACE_MALLOC_XXX
273 NS_GetStackTrace(1)
274#else
275 nsnull
276#endif
277 ;
278 nsNamedVector* vec1;
279 nsNamedVector* vec2;
280 PRUint32 i2;
281
282 if (!WellOrdered(stackTop->mAddr, addr, node, &i2, &vec1, &vec2)) {
283 char buf[128];
284 PR_snprintf(buf, sizeof buf,
285 "Potential deadlock between %s%s@%p and %s%s@%p",
286 vec1->mName ? vec1->mName : "",
287 LockTypeNames[stackTop->mType],
288 stackTop->mAddr,
289 vec2->mName ? vec2->mName : "",
290 LockTypeNames[type],
291 addr);
292#ifdef NS_TRACE_MALLOC_XXX
293 fprintf(stderr, "\n*** %s\n\nCurrent stack:\n", buf);
294 NS_DumpStackTrace(node, stderr);
295
296 fputs("\nPrevious stack:\n", stderr);
297 NS_DumpStackTrace(vec2->mInnerSites.ElementAt(i2), stderr);
298 putc('\n', stderr);
299#endif
300 NS_ERROR(buf);
301 }
302 }
303 }
304
305 mAddr = addr;
306 mDown = stackTop;
307 mType = type;
308 if (mAddr)
309 (void) PR_SetThreadPrivate(LockStackTPI, this);
310}
311
312nsAutoLockBase::~nsAutoLockBase()
313{
314 if (mAddr)
315 (void) PR_SetThreadPrivate(LockStackTPI, mDown);
316}
317
318void nsAutoLockBase::Show()
319{
320 if (!mAddr)
321 return;
322 nsAutoLockBase* curr = (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI);
323 nsAutoLockBase* prev = nsnull;
324 while (curr != mDown) {
325 prev = curr;
326 curr = prev->mDown;
327 }
328 if (!prev)
329 PR_SetThreadPrivate(LockStackTPI, this);
330 else
331 prev->mDown = this;
332}
333
334void nsAutoLockBase::Hide()
335{
336 if (!mAddr)
337 return;
338 nsAutoLockBase* curr = (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI);
339 nsAutoLockBase* prev = nsnull;
340 while (curr != this) {
341 prev = curr;
342 curr = prev->mDown;
343 }
344 if (!prev)
345 PR_SetThreadPrivate(LockStackTPI, mDown);
346 else
347 prev->mDown = mDown;
348}
349
350#endif /* DEBUG */
351
352PRMonitor* nsAutoMonitor::NewMonitor(const char* name)
353{
354 PRMonitor* mon = PR_NewMonitor();
355#ifdef DEBUG
356 if (mon && OrderTable) {
357 nsNamedVector* value = new nsNamedVector(name);
358 if (value) {
359 PR_Lock(OrderTableLock);
360 PL_HashTableAdd(OrderTable, mon, value);
361 PR_Unlock(OrderTableLock);
362 }
363 }
364#endif
365 return mon;
366}
367
368void nsAutoMonitor::DestroyMonitor(PRMonitor* mon)
369{
370#ifdef DEBUG
371 if (OrderTable)
372 OnMonitorRecycle(mon);
373#endif
374 PR_DestroyMonitor(mon);
375}
376
377void nsAutoMonitor::Enter()
378{
379#ifdef DEBUG
380 if (!mAddr) {
381 NS_ERROR("It is not legal to enter a null monitor");
382 return;
383 }
384 nsAutoLockBase* stackTop =
385 (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI);
386 NS_ASSERTION(stackTop == mDown, "non-LIFO nsAutoMonitor::Enter");
387 mDown = stackTop;
388 (void) PR_SetThreadPrivate(LockStackTPI, this);
389#endif
390 PR_EnterMonitor(mMonitor);
391 mLockCount += 1;
392}
393
394void nsAutoMonitor::Exit()
395{
396#ifdef DEBUG
397 if (!mAddr) {
398 NS_ERROR("It is not legal to exit a null monitor");
399 return;
400 }
401 (void) PR_SetThreadPrivate(LockStackTPI, mDown);
402#endif
403 PRStatus status = PR_ExitMonitor(mMonitor);
404 NS_ASSERTION(status == PR_SUCCESS, "PR_ExitMonitor failed");
405 mLockCount -= 1;
406}
407
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