VirtualBox

source: vbox/trunk/src/VBox/VMM/PDMCritSect.cpp@ 25368

Last change on this file since 25368 was 25368, checked in by vboxsync, 15 years ago

RTCritSect,PDMCritSect,iprt/lockvalidator.h: Reworked the deadlocking detection for critical sections and preparing for lock order validation. This change generalizes the RTCRITSECT::Strict data and moves it out of the RTCRITSECT, leaving a pointer behind. This saves a bit of space in release builds.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.1 KB
Line 
1/* $Id: PDMCritSect.cpp 25368 2009-12-14 16:31:40Z vboxsync $ */
2/** @file
3 * PDM - Critical Sections, Ring-3.
4 */
5
6/*
7 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_PDM//_CRITSECT
27#include "PDMInternal.h"
28#include <VBox/pdmcritsect.h>
29#include <VBox/mm.h>
30#include <VBox/vm.h>
31
32#include <VBox/err.h>
33#include <VBox/log.h>
34#include <VBox/sup.h>
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/string.h>
38#include <iprt/thread.h>
39
40
41/*******************************************************************************
42* Internal Functions *
43*******************************************************************************/
44static int pdmR3CritSectDeleteOne(PVM pVM, PPDMCRITSECTINT pCritSect, PPDMCRITSECTINT pPrev, bool fFinal);
45
46
47
48/**
49 * Initializes the critical section subcomponent.
50 *
51 * @returns VBox status code.
52 * @param pVM The VM handle.
53 * @remark Not to be confused with PDMR3CritSectInit and pdmR3CritSectInitDevice which are
54 * for initializing a critical section.
55 */
56int pdmR3CritSectInit(PVM pVM)
57{
58 STAM_REG(pVM, &pVM->pdm.s.StatQueuedCritSectLeaves, STAMTYPE_COUNTER, "/PDM/QueuedCritSectLeaves", STAMUNIT_OCCURENCES,
59 "Number of times a critical section leave requesed needed to be queued for ring-3 execution.");
60 return VINF_SUCCESS;
61}
62
63
64/**
65 * Relocates all the critical sections.
66 *
67 * @param pVM The VM handle.
68 */
69void pdmR3CritSectRelocate(PVM pVM)
70{
71 RTCritSectEnter(&pVM->pdm.s.MiscCritSect);
72 for (PPDMCRITSECTINT pCur = pVM->pdm.s.pCritSects;
73 pCur;
74 pCur = pCur->pNext)
75 pCur->pVMRC = pVM->pVMRC;
76 RTCritSectLeave(&pVM->pdm.s.MiscCritSect);
77}
78
79
80/**
81 * Deletes all remaining critical sections.
82 *
83 * This is called at the end of the termination process.
84 *
85 * @returns VBox status.
86 * First error code, rest is lost.
87 * @param pVM The VM handle.
88 * @remark Don't confuse this with PDMR3CritSectDelete.
89 */
90VMMDECL(int) PDMR3CritSectTerm(PVM pVM)
91{
92 int rc = VINF_SUCCESS;
93 RTCritSectEnter(&pVM->pdm.s.MiscCritSect);
94 while (pVM->pdm.s.pCritSects)
95 {
96 int rc2 = pdmR3CritSectDeleteOne(pVM, pVM->pdm.s.pCritSects, NULL, true /* final */);
97 AssertRC(rc2);
98 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
99 rc = rc2;
100 }
101 RTCritSectLeave(&pVM->pdm.s.MiscCritSect);
102 RTCritSectDelete(&pVM->pdm.s.MiscCritSect);
103 return rc;
104}
105
106
107
108/**
109 * Initalizes a critical section and inserts it into the list.
110 *
111 * @returns VBox status code.
112 * @param pVM The Vm handle.
113 * @param pCritSect The critical section.
114 * @param pvKey The owner key.
115 * @param pszName The name of the critical section (for statistics).
116 */
117static int pdmR3CritSectInitOne(PVM pVM, PPDMCRITSECTINT pCritSect, void *pvKey, const char *pszName)
118{
119 VM_ASSERT_EMT(pVM);
120
121 /*
122 * Allocate the semaphore.
123 */
124 AssertCompile(sizeof(SUPSEMEVENT) == sizeof(pCritSect->Core.EventSem));
125 int rc = SUPSemEventCreate(pVM->pSession, (PSUPSEMEVENT)&pCritSect->Core.EventSem);
126 if (RT_SUCCESS(rc))
127 {
128 rc = RTLockValidatorCreate(&pCritSect->Core.pValidatorRec, NIL_RTLOCKVALIDATORCLASS, 0, pszName, pCritSect);
129 if (RT_SUCCESS(rc))
130 {
131 /*
132 * Initialize the structure (first bit is c&p from RTCritSectInitEx).
133 */
134 pCritSect->Core.u32Magic = RTCRITSECT_MAGIC;
135 pCritSect->Core.fFlags = 0;
136 pCritSect->Core.cNestings = 0;
137 pCritSect->Core.cLockers = -1;
138 pCritSect->Core.NativeThreadOwner = NIL_RTNATIVETHREAD;
139 pCritSect->pVMR3 = pVM;
140 pCritSect->pVMR0 = pVM->pVMR0;
141 pCritSect->pVMRC = pVM->pVMRC;
142 pCritSect->pvKey = pvKey;
143 pCritSect->EventToSignal = NIL_RTSEMEVENT;
144 pCritSect->pNext = pVM->pdm.s.pCritSects;
145 pCritSect->pszName = RTStrDup(pszName);
146 pVM->pdm.s.pCritSects = pCritSect;
147 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZLock, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionRZLock", pszName);
148 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZUnlock,STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionRZUnlock", pszName);
149 STAMR3RegisterF(pVM, &pCritSect->StatContentionR3, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionR3", pszName);
150#ifdef VBOX_WITH_STATISTICS
151 STAMR3RegisterF(pVM, &pCritSect->StatLocked, STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, NULL, "/PDM/CritSects/%s/Locked", pszName);
152#endif
153 return VINF_SUCCESS;
154 }
155
156 SUPSemEventClose(pVM->pSession, (SUPSEMEVENT)pCritSect->Core.EventSem);
157 }
158 return rc;
159}
160
161
162/**
163 * Initializes a PDM critical section for internal use.
164 *
165 * The PDM critical sections are derived from the IPRT critical sections, but
166 * works in GC as well.
167 *
168 * @returns VBox status code.
169 * @param pVM The VM handle.
170 * @param pDevIns Device instance.
171 * @param pCritSect Pointer to the critical section.
172 * @param pszName The name of the critical section (for statistics).
173 * @thread EMT(0)
174 */
175VMMR3DECL(int) PDMR3CritSectInit(PVM pVM, PPDMCRITSECT pCritSect, const char *pszName)
176{
177#if HC_ARCH_BITS == 64 && GC_ARCH_BITS == 32
178 AssertCompile(sizeof(pCritSect->padding) >= sizeof(pCritSect->s));
179#endif
180 Assert(RT_ALIGN_P(pCritSect, sizeof(uintptr_t)) == pCritSect);
181 return pdmR3CritSectInitOne(pVM, &pCritSect->s, pCritSect, pszName);
182}
183
184
185/**
186 * Initializes a PDM critical section.
187 *
188 * The PDM critical sections are derived from the IPRT critical sections, but
189 * works in GC as well.
190 *
191 * @returns VBox status code.
192 * @param pVM The VM handle.
193 * @param pDevIns Device instance.
194 * @param pCritSect Pointer to the critical section.
195 * @param pszName The name of the critical section (for statistics).
196 */
197int pdmR3CritSectInitDevice(PVM pVM, PPDMDEVINS pDevIns, PPDMCRITSECT pCritSect, const char *pszName)
198{
199 return pdmR3CritSectInitOne(pVM, &pCritSect->s, pDevIns, pszName);
200}
201
202
203/**
204 * Deletes one critical section.
205 *
206 * @returns Return code from RTCritSectDelete.
207 *
208 * @param pVM The VM handle.
209 * @param pCritSect The critical section.
210 * @param pPrev The previous critical section in the list.
211 * @param fFinal Set if this is the final call and statistics shouldn't be deregistered.
212 *
213 * @remarks Caller must've entered the MiscCritSect.
214 */
215static int pdmR3CritSectDeleteOne(PVM pVM, PPDMCRITSECTINT pCritSect, PPDMCRITSECTINT pPrev, bool fFinal)
216{
217 /*
218 * Assert free waiters and so on (c&p from RTCritSectDelete).
219 */
220 Assert(pCritSect->Core.u32Magic == RTCRITSECT_MAGIC);
221 Assert(pCritSect->Core.cNestings == 0);
222 Assert(pCritSect->Core.cLockers == -1);
223 Assert(pCritSect->Core.NativeThreadOwner == NIL_RTNATIVETHREAD);
224 Assert(RTCritSectIsOwner(&pVM->pdm.s.MiscCritSect));
225
226 /*
227 * Unlink it.
228 */
229 if (pPrev)
230 pPrev->pNext = pCritSect->pNext;
231 else
232 pVM->pdm.s.pCritSects = pCritSect->pNext;
233
234 /*
235 * Delete it (parts taken from RTCritSectDelete).
236 * In case someone is waiting we'll signal the semaphore cLockers + 1 times.
237 */
238 ASMAtomicWriteU32(&pCritSect->Core.u32Magic, 0);
239 SUPSEMEVENT hEvent = (SUPSEMEVENT)pCritSect->Core.EventSem;
240 pCritSect->Core.EventSem = NIL_RTSEMEVENT;
241 while (pCritSect->Core.cLockers-- >= 0)
242 SUPSemEventSignal(pVM->pSession, hEvent);
243 ASMAtomicWriteS32(&pCritSect->Core.cLockers, -1);
244 int rc = SUPSemEventClose(pVM->pSession, hEvent);
245 AssertRC(rc);
246 RTLockValidatorDestroy(&pCritSect->Core.pValidatorRec);
247 pCritSect->pNext = NULL;
248 pCritSect->pvKey = NULL;
249 pCritSect->pVMR3 = NULL;
250 pCritSect->pVMR0 = NIL_RTR0PTR;
251 pCritSect->pVMRC = NIL_RTRCPTR;
252 RTStrFree((char *)pCritSect->pszName);
253 pCritSect->pszName = NULL;
254 if (!fFinal)
255 {
256 STAMR3Deregister(pVM, &pCritSect->StatContentionRZLock);
257 STAMR3Deregister(pVM, &pCritSect->StatContentionRZUnlock);
258 STAMR3Deregister(pVM, &pCritSect->StatContentionR3);
259#ifdef VBOX_WITH_STATISTICS
260 STAMR3Deregister(pVM, &pCritSect->StatLocked);
261#endif
262 }
263 return rc;
264}
265
266
267/**
268 * Deletes all critical sections with a give initializer key.
269 *
270 * @returns VBox status code.
271 * The entire list is processed on failure, so we'll only
272 * return the first error code. This shouldn't be a problem
273 * since errors really shouldn't happen here.
274 * @param pVM The VM handle.
275 * @param pvKey The initializer key.
276 */
277static int pdmR3CritSectDeleteByKey(PVM pVM, void *pvKey)
278{
279 /*
280 * Iterate the list and match key.
281 */
282 int rc = VINF_SUCCESS;
283 PPDMCRITSECTINT pPrev = NULL;
284 RTCritSectEnter(&pVM->pdm.s.MiscCritSect);
285 PPDMCRITSECTINT pCur = pVM->pdm.s.pCritSects;
286 while (pCur)
287 {
288 if (pCur->pvKey == pvKey)
289 {
290 int rc2 = pdmR3CritSectDeleteOne(pVM, pCur, pPrev, false /* not final */);
291 AssertRC(rc2);
292 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
293 rc = rc2;
294 }
295
296 /* next */
297 pPrev = pCur;
298 pCur = pCur->pNext;
299 }
300 RTCritSectLeave(&pVM->pdm.s.MiscCritSect);
301 return rc;
302}
303
304
305/**
306 * Deletes all undeleted critical sections initalized by a given device.
307 *
308 * @returns VBox status code.
309 * @param pVM The VM handle.
310 * @param pDevIns The device handle.
311 */
312int pdmR3CritSectDeleteDevice(PVM pVM, PPDMDEVINS pDevIns)
313{
314 return pdmR3CritSectDeleteByKey(pVM, pDevIns);
315}
316
317
318/**
319 * Deletes the critical section.
320 *
321 * @returns VBox status code.
322 * @param pCritSect The PDM critical section to destroy.
323 */
324VMMR3DECL(int) PDMR3CritSectDelete(PPDMCRITSECT pCritSect)
325{
326 if (!RTCritSectIsInitialized(&pCritSect->s.Core))
327 return VINF_SUCCESS;
328
329 /*
330 * Find and unlink it.
331 */
332 PVM pVM = pCritSect->s.pVMR3;
333 AssertReleaseReturn(pVM, VERR_INTERNAL_ERROR);
334 PPDMCRITSECTINT pPrev = NULL;
335 RTCritSectEnter(&pVM->pdm.s.MiscCritSect);
336 PPDMCRITSECTINT pCur = pVM->pdm.s.pCritSects;
337 while (pCur)
338 {
339 if (pCur == &pCritSect->s)
340 {
341 int rc = pdmR3CritSectDeleteOne(pVM, pCur, pPrev, false /* not final */);
342 RTCritSectLeave(&pVM->pdm.s.MiscCritSect);
343 return rc;
344 }
345
346 /* next */
347 pPrev = pCur;
348 pCur = pCur->pNext;
349 }
350 RTCritSectLeave(&pVM->pdm.s.MiscCritSect);
351 AssertReleaseMsgFailed(("pCritSect=%p wasn't found!\n", pCritSect));
352 return VERR_INTERNAL_ERROR;
353}
354
355
356/**
357 * Gets the name of the critical section.
358 *
359 *
360 * @returns Pointer to the critical section name (read only) on success,
361 * NULL on failure (invalid critical section).
362 * @param pCritSect The critical section.
363 */
364VMMR3DECL(const char *) PDMR3CritSectName(PCPDMCRITSECT pCritSect)
365{
366 AssertPtrReturn(pCritSect, NULL);
367 AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, NULL);
368 return pCritSect->s.pszName;
369}
370
371
372/**
373 * Yield the critical section if someone is waiting on it.
374 *
375 * When yielding, we'll leave the critical section and try to make sure the
376 * other waiting threads get a chance of entering before we reclaim it.
377 *
378 * @retval true if yielded.
379 * @retval false if not yielded.
380 * @param pCritSect The critical section.
381 */
382VMMR3DECL(bool) PDMR3CritSectYield(PPDMCRITSECT pCritSect)
383{
384 AssertPtrReturn(pCritSect, false);
385 AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, false);
386 Assert(pCritSect->s.Core.NativeThreadOwner == RTThreadNativeSelf());
387
388 /* No recursion allowed here. */
389 int32_t const cNestings = pCritSect->s.Core.cNestings;
390 AssertReturn(cNestings == 1, false);
391
392 int32_t const cLockers = ASMAtomicReadS32(&pCritSect->s.Core.cLockers);
393 if (cLockers < cNestings)
394 return false;
395
396#ifdef PDMCRITSECT_STRICT
397 const char * const pszFile = pCritSect->s.Core.pValidatorRec->pszFile;
398 const char * const pszFunction = pCritSect->s.Core.pValidatorRec->pszFunction;
399 uint32_t const iLine = pCritSect->s.Core.pValidatorRec->uLine;
400 RTHCUINTPTR const uId = pCritSect->s.Core.pValidatorRec->uId;
401#endif
402 PDMCritSectLeave(pCritSect);
403
404 /*
405 * If we're lucky, then one of the waiters has entered the lock already.
406 * We spin a little bit in hope for this to happen so we can avoid the
407 * yield deatour.
408 */
409 if (ASMAtomicUoReadS32(&pCritSect->s.Core.cNestings) == 0)
410 {
411 int cLoops = 20;
412 while ( cLoops > 0
413 && ASMAtomicUoReadS32(&pCritSect->s.Core.cNestings) == 0
414 && ASMAtomicUoReadS32(&pCritSect->s.Core.cLockers) >= 0)
415 {
416 ASMNopPause();
417 cLoops--;
418 }
419 if (cLoops == 0)
420 RTThreadYield();
421 }
422
423#ifdef PDMCRITSECT_STRICT
424 int rc = PDMCritSectEnterDebug(pCritSect, VERR_INTERNAL_ERROR, uId, pszFile, iLine, pszFunction);
425#else
426 int rc = PDMCritSectEnter(pCritSect, VERR_INTERNAL_ERROR);
427#endif
428 AssertLogRelRC(rc);
429 return true;
430}
431
432
433/**
434 * Schedule a event semaphore for signalling upon critsect exit.
435 *
436 * @returns VINF_SUCCESS on success.
437 * @returns VERR_TOO_MANY_SEMAPHORES if an event was already scheduled.
438 * @returns VERR_NOT_OWNER if we're not the critsect owner.
439 * @returns VERR_SEM_DESTROYED if RTCritSectDelete was called while waiting.
440 *
441 * @param pCritSect The critical section.
442 * @param EventToSignal The semapore that should be signalled.
443 */
444VMMR3DECL(int) PDMR3CritSectScheduleExitEvent(PPDMCRITSECT pCritSect, RTSEMEVENT EventToSignal)
445{
446 Assert(EventToSignal != NIL_RTSEMEVENT);
447 if (RT_UNLIKELY(!RTCritSectIsOwner(&pCritSect->s.Core)))
448 return VERR_NOT_OWNER;
449 if (RT_LIKELY( pCritSect->s.EventToSignal == NIL_RTSEMEVENT
450 || pCritSect->s.EventToSignal == EventToSignal))
451 {
452 pCritSect->s.EventToSignal = EventToSignal;
453 return VINF_SUCCESS;
454 }
455 return VERR_TOO_MANY_SEMAPHORES;
456}
457
458
459/**
460 * Counts the critical sections owned by the calling thread, optionally
461 * returning a comma separated list naming them.
462 *
463 * This is for diagnostic purposes only.
464 *
465 * @returns Lock count.
466 *
467 * @param pVM The VM handle.
468 * @param pszNames Where to return the critical section names.
469 * @param cbNames The size of the buffer.
470 */
471VMMR3DECL(uint32_t) PDMR3CritSectCountOwned(PVM pVM, char *pszNames, size_t cbNames)
472{
473 /*
474 * Init the name buffer.
475 */
476 size_t cchLeft = cbNames;
477 if (cchLeft)
478 {
479 cchLeft--;
480 pszNames[0] = pszNames[cchLeft] = '\0';
481 }
482
483 /*
484 * Iterate the critical sections.
485 */
486 /* This is unsafe, but wtf. */
487 RTNATIVETHREAD const hNativeThread = RTThreadNativeSelf();
488 uint32_t cCritSects = 0;
489 for (PPDMCRITSECTINT pCur = pVM->pdm.s.pCritSects;
490 pCur;
491 pCur = pCur->pNext)
492 {
493 /* Same as RTCritSectIsOwner(). */
494 if (pCur->Core.NativeThreadOwner == hNativeThread)
495 {
496 cCritSects++;
497
498 /*
499 * Copy the name if there is space. Fun stuff.
500 */
501 if (cchLeft)
502 {
503 /* try add comma. */
504 if (cCritSects != 1)
505 {
506 *pszNames++ = ',';
507 if (--cchLeft)
508 {
509 *pszNames++ = ' ';
510 cchLeft--;
511 }
512 }
513
514 /* try copy the name. */
515 if (cchLeft)
516 {
517 size_t const cchName = strlen(pCur->pszName);
518 if (cchName < cchLeft)
519 {
520 memcpy(pszNames, pCur->pszName, cchName);
521 pszNames += cchName;
522 cchLeft -= cchName;
523 }
524 else
525 {
526 if (cchLeft > 2)
527 {
528 memcpy(pszNames, pCur->pszName, cchLeft - 2);
529 pszNames += cchLeft - 2;
530 cchLeft = 2;
531 }
532 while (cchLeft-- > 0)
533 *pszNames++ = '+';
534 }
535 }
536 *pszNames = '\0';
537 }
538 }
539 }
540
541 return cCritSects;
542}
543
544
545/**
546 * Leave all critical sections the calling thread owns.
547 *
548 * @param pVM The VM handle.
549 */
550void PDMR3CritSectLeaveAll(PVM pVM)
551{
552 RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
553
554 RTCritSectEnter(&pVM->pdm.s.MiscCritSect);
555 for (PPDMCRITSECTINT pCur = pVM->pdm.s.pCritSects;
556 pCur;
557 pCur = pCur->pNext)
558 {
559 while ( pCur->Core.NativeThreadOwner == hNativeSelf
560 && pCur->Core.cNestings > 0)
561 PDMCritSectLeave((PPDMCRITSECT)pCur);
562 }
563 RTCritSectLeave(&pVM->pdm.s.MiscCritSect);
564}
565
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