VirtualBox

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

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

iprt/cdefs,*: Use RT_LOCK_STRICT and RT_LOCK_STRICT_ORDER for controlling deadlock detection and lock order validation. Currently both are disabled by default, but it's possible to add VBOX_WITH_STRICT_LOCKS=1 to LocalConfig.kmk to enable it all.

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