VirtualBox

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

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

IPRT,PDMCritSect: Lock validation can only be performed in ring-3; fixed #PF on 32-bit darwin with debug builds. Hopefully fixed the recursion issue on windows.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.1 KB
Line 
1/* $Id: PDMCritSect.cpp 25406 2009-12-15 14:23:53Z 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 pszName The name of the critical section (for statistics).
117 */
118static int pdmR3CritSectInitOne(PVM pVM, PPDMCRITSECTINT pCritSect, void *pvKey, const char *pszName)
119{
120 VM_ASSERT_EMT(pVM);
121
122 /*
123 * Allocate the semaphore.
124 */
125 AssertCompile(sizeof(SUPSEMEVENT) == sizeof(pCritSect->Core.EventSem));
126 int rc = SUPSemEventCreate(pVM->pSession, (PSUPSEMEVENT)&pCritSect->Core.EventSem);
127 if (RT_SUCCESS(rc))
128 {
129 rc = RTLockValidatorCreate(&pCritSect->Core.pValidatorRec, NIL_RTLOCKVALIDATORCLASS, 0, pszName, pCritSect);
130 if (RT_SUCCESS(rc))
131 {
132 /*
133 * Initialize the structure (first bit is c&p from RTCritSectInitEx).
134 */
135 pCritSect->Core.u32Magic = RTCRITSECT_MAGIC;
136 pCritSect->Core.fFlags = 0;
137 pCritSect->Core.cNestings = 0;
138 pCritSect->Core.cLockers = -1;
139 pCritSect->Core.NativeThreadOwner = NIL_RTNATIVETHREAD;
140 pCritSect->pVMR3 = pVM;
141 pCritSect->pVMR0 = pVM->pVMR0;
142 pCritSect->pVMRC = pVM->pVMRC;
143 pCritSect->pvKey = pvKey;
144 pCritSect->EventToSignal = NIL_RTSEMEVENT;
145 pCritSect->pNext = pVM->pdm.s.pCritSects;
146 pCritSect->pszName = RTStrDup(pszName);
147 pVM->pdm.s.pCritSects = pCritSect;
148 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZLock, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionRZLock", pszName);
149 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZUnlock,STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionRZUnlock", pszName);
150 STAMR3RegisterF(pVM, &pCritSect->StatContentionR3, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionR3", pszName);
151#ifdef VBOX_WITH_STATISTICS
152 STAMR3RegisterF(pVM, &pCritSect->StatLocked, STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, NULL, "/PDM/CritSects/%s/Locked", pszName);
153#endif
154 return VINF_SUCCESS;
155 }
156
157 SUPSemEventClose(pVM->pSession, (SUPSEMEVENT)pCritSect->Core.EventSem);
158 }
159 return rc;
160}
161
162
163/**
164 * Initializes a PDM critical section for internal use.
165 *
166 * The PDM critical sections are derived from the IPRT critical sections, but
167 * works in GC as well.
168 *
169 * @returns VBox status code.
170 * @param pVM The VM handle.
171 * @param pDevIns Device instance.
172 * @param pCritSect Pointer to the critical section.
173 * @param pszName The name of the critical section (for statistics).
174 * @thread EMT(0)
175 */
176VMMR3DECL(int) PDMR3CritSectInit(PVM pVM, PPDMCRITSECT pCritSect, const char *pszName)
177{
178#if HC_ARCH_BITS == 64 && GC_ARCH_BITS == 32
179 AssertCompile(sizeof(pCritSect->padding) >= sizeof(pCritSect->s));
180#endif
181 Assert(RT_ALIGN_P(pCritSect, sizeof(uintptr_t)) == pCritSect);
182 return pdmR3CritSectInitOne(pVM, &pCritSect->s, pCritSect, pszName);
183}
184
185
186/**
187 * Initializes a PDM critical section.
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 pszName The name of the critical section (for statistics).
197 */
198int pdmR3CritSectInitDevice(PVM pVM, PPDMDEVINS pDevIns, PPDMCRITSECT pCritSect, const char *pszName)
199{
200 return pdmR3CritSectInitOne(pVM, &pCritSect->s, pDevIns, pszName);
201}
202
203
204/**
205 * Deletes one critical section.
206 *
207 * @returns Return code from RTCritSectDelete.
208 *
209 * @param pVM The VM handle.
210 * @param pCritSect The critical section.
211 * @param pPrev The previous critical section in the list.
212 * @param fFinal Set if this is the final call and statistics shouldn't be deregistered.
213 *
214 * @remarks Caller must've entered the MiscCritSect.
215 */
216static int pdmR3CritSectDeleteOne(PVM pVM, PPDMCRITSECTINT pCritSect, PPDMCRITSECTINT pPrev, bool fFinal)
217{
218 /*
219 * Assert free waiters and so on (c&p from RTCritSectDelete).
220 */
221 Assert(pCritSect->Core.u32Magic == RTCRITSECT_MAGIC);
222 Assert(pCritSect->Core.cNestings == 0);
223 Assert(pCritSect->Core.cLockers == -1);
224 Assert(pCritSect->Core.NativeThreadOwner == NIL_RTNATIVETHREAD);
225 Assert(RTCritSectIsOwner(&pVM->pdm.s.MiscCritSect));
226
227 /*
228 * Unlink it.
229 */
230 if (pPrev)
231 pPrev->pNext = pCritSect->pNext;
232 else
233 pVM->pdm.s.pCritSects = pCritSect->pNext;
234
235 /*
236 * Delete it (parts taken from RTCritSectDelete).
237 * In case someone is waiting we'll signal the semaphore cLockers + 1 times.
238 */
239 ASMAtomicWriteU32(&pCritSect->Core.u32Magic, 0);
240 SUPSEMEVENT hEvent = (SUPSEMEVENT)pCritSect->Core.EventSem;
241 pCritSect->Core.EventSem = NIL_RTSEMEVENT;
242 while (pCritSect->Core.cLockers-- >= 0)
243 SUPSemEventSignal(pVM->pSession, hEvent);
244 ASMAtomicWriteS32(&pCritSect->Core.cLockers, -1);
245 int rc = SUPSemEventClose(pVM->pSession, hEvent);
246 AssertRC(rc);
247 RTLockValidatorDestroy(&pCritSect->Core.pValidatorRec);
248 pCritSect->pNext = NULL;
249 pCritSect->pvKey = NULL;
250 pCritSect->pVMR3 = NULL;
251 pCritSect->pVMR0 = NIL_RTR0PTR;
252 pCritSect->pVMRC = NIL_RTRCPTR;
253 RTStrFree((char *)pCritSect->pszName);
254 pCritSect->pszName = NULL;
255 if (!fFinal)
256 {
257 STAMR3Deregister(pVM, &pCritSect->StatContentionRZLock);
258 STAMR3Deregister(pVM, &pCritSect->StatContentionRZUnlock);
259 STAMR3Deregister(pVM, &pCritSect->StatContentionR3);
260#ifdef VBOX_WITH_STATISTICS
261 STAMR3Deregister(pVM, &pCritSect->StatLocked);
262#endif
263 }
264 return rc;
265}
266
267
268/**
269 * Deletes all critical sections with a give initializer key.
270 *
271 * @returns VBox status code.
272 * The entire list is processed on failure, so we'll only
273 * return the first error code. This shouldn't be a problem
274 * since errors really shouldn't happen here.
275 * @param pVM The VM handle.
276 * @param pvKey The initializer key.
277 */
278static int pdmR3CritSectDeleteByKey(PVM pVM, void *pvKey)
279{
280 /*
281 * Iterate the list and match key.
282 */
283 int rc = VINF_SUCCESS;
284 PPDMCRITSECTINT pPrev = NULL;
285 RTCritSectEnter(&pVM->pdm.s.MiscCritSect);
286 PPDMCRITSECTINT pCur = pVM->pdm.s.pCritSects;
287 while (pCur)
288 {
289 if (pCur->pvKey == pvKey)
290 {
291 int rc2 = pdmR3CritSectDeleteOne(pVM, pCur, pPrev, false /* not final */);
292 AssertRC(rc2);
293 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
294 rc = rc2;
295 }
296
297 /* next */
298 pPrev = pCur;
299 pCur = pCur->pNext;
300 }
301 RTCritSectLeave(&pVM->pdm.s.MiscCritSect);
302 return rc;
303}
304
305
306/**
307 * Deletes all undeleted critical sections initalized by a given device.
308 *
309 * @returns VBox status code.
310 * @param pVM The VM handle.
311 * @param pDevIns The device handle.
312 */
313int pdmR3CritSectDeleteDevice(PVM pVM, PPDMDEVINS pDevIns)
314{
315 return pdmR3CritSectDeleteByKey(pVM, pDevIns);
316}
317
318
319/**
320 * Deletes the critical section.
321 *
322 * @returns VBox status code.
323 * @param pCritSect The PDM critical section to destroy.
324 */
325VMMR3DECL(int) PDMR3CritSectDelete(PPDMCRITSECT pCritSect)
326{
327 if (!RTCritSectIsInitialized(&pCritSect->s.Core))
328 return VINF_SUCCESS;
329
330 /*
331 * Find and unlink it.
332 */
333 PVM pVM = pCritSect->s.pVMR3;
334 AssertReleaseReturn(pVM, VERR_INTERNAL_ERROR);
335 PPDMCRITSECTINT pPrev = NULL;
336 RTCritSectEnter(&pVM->pdm.s.MiscCritSect);
337 PPDMCRITSECTINT pCur = pVM->pdm.s.pCritSects;
338 while (pCur)
339 {
340 if (pCur == &pCritSect->s)
341 {
342 int rc = pdmR3CritSectDeleteOne(pVM, pCur, pPrev, false /* not final */);
343 RTCritSectLeave(&pVM->pdm.s.MiscCritSect);
344 return rc;
345 }
346
347 /* next */
348 pPrev = pCur;
349 pCur = pCur->pNext;
350 }
351 RTCritSectLeave(&pVM->pdm.s.MiscCritSect);
352 AssertReleaseMsgFailed(("pCritSect=%p wasn't found!\n", pCritSect));
353 return VERR_INTERNAL_ERROR;
354}
355
356
357/**
358 * Gets the name of the critical section.
359 *
360 *
361 * @returns Pointer to the critical section name (read only) on success,
362 * NULL on failure (invalid critical section).
363 * @param pCritSect The critical section.
364 */
365VMMR3DECL(const char *) PDMR3CritSectName(PCPDMCRITSECT pCritSect)
366{
367 AssertPtrReturn(pCritSect, NULL);
368 AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, NULL);
369 return pCritSect->s.pszName;
370}
371
372
373/**
374 * Yield the critical section if someone is waiting on it.
375 *
376 * When yielding, we'll leave the critical section and try to make sure the
377 * other waiting threads get a chance of entering before we reclaim it.
378 *
379 * @retval true if yielded.
380 * @retval false if not yielded.
381 * @param pCritSect The critical section.
382 */
383VMMR3DECL(bool) PDMR3CritSectYield(PPDMCRITSECT pCritSect)
384{
385 AssertPtrReturn(pCritSect, false);
386 AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, false);
387 Assert(pCritSect->s.Core.NativeThreadOwner == RTThreadNativeSelf());
388
389 /* No recursion allowed here. */
390 int32_t const cNestings = pCritSect->s.Core.cNestings;
391 AssertReturn(cNestings == 1, false);
392
393 int32_t const cLockers = ASMAtomicReadS32(&pCritSect->s.Core.cLockers);
394 if (cLockers < cNestings)
395 return false;
396
397#ifdef PDMCRITSECT_STRICT
398 const char * const pszFile = pCritSect->s.Core.pValidatorRec->pszFile;
399 const char * const pszFunction = pCritSect->s.Core.pValidatorRec->pszFunction;
400 uint32_t const iLine = pCritSect->s.Core.pValidatorRec->uLine;
401 RTHCUINTPTR const uId = pCritSect->s.Core.pValidatorRec->uId;
402#endif
403 PDMCritSectLeave(pCritSect);
404
405 /*
406 * If we're lucky, then one of the waiters has entered the lock already.
407 * We spin a little bit in hope for this to happen so we can avoid the
408 * yield deatour.
409 */
410 if (ASMAtomicUoReadS32(&pCritSect->s.Core.cNestings) == 0)
411 {
412 int cLoops = 20;
413 while ( cLoops > 0
414 && ASMAtomicUoReadS32(&pCritSect->s.Core.cNestings) == 0
415 && ASMAtomicUoReadS32(&pCritSect->s.Core.cLockers) >= 0)
416 {
417 ASMNopPause();
418 cLoops--;
419 }
420 if (cLoops == 0)
421 RTThreadYield();
422 }
423
424#ifdef PDMCRITSECT_STRICT
425 int rc = PDMCritSectEnterDebug(pCritSect, VERR_INTERNAL_ERROR, uId, pszFile, iLine, pszFunction);
426#else
427 int rc = PDMCritSectEnter(pCritSect, VERR_INTERNAL_ERROR);
428#endif
429 AssertLogRelRC(rc);
430 return true;
431}
432
433
434/**
435 * Schedule a event semaphore for signalling upon critsect exit.
436 *
437 * @returns VINF_SUCCESS on success.
438 * @returns VERR_TOO_MANY_SEMAPHORES if an event was already scheduled.
439 * @returns VERR_NOT_OWNER if we're not the critsect owner.
440 * @returns VERR_SEM_DESTROYED if RTCritSectDelete was called while waiting.
441 *
442 * @param pCritSect The critical section.
443 * @param EventToSignal The semapore that should be signalled.
444 */
445VMMR3DECL(int) PDMR3CritSectScheduleExitEvent(PPDMCRITSECT pCritSect, RTSEMEVENT EventToSignal)
446{
447 Assert(EventToSignal != NIL_RTSEMEVENT);
448 if (RT_UNLIKELY(!RTCritSectIsOwner(&pCritSect->s.Core)))
449 return VERR_NOT_OWNER;
450 if (RT_LIKELY( pCritSect->s.EventToSignal == NIL_RTSEMEVENT
451 || pCritSect->s.EventToSignal == EventToSignal))
452 {
453 pCritSect->s.EventToSignal = EventToSignal;
454 return VINF_SUCCESS;
455 }
456 return VERR_TOO_MANY_SEMAPHORES;
457}
458
459
460/**
461 * Counts the critical sections owned by the calling thread, optionally
462 * returning a comma separated list naming them.
463 *
464 * This is for diagnostic purposes only.
465 *
466 * @returns Lock count.
467 *
468 * @param pVM The VM handle.
469 * @param pszNames Where to return the critical section names.
470 * @param cbNames The size of the buffer.
471 */
472VMMR3DECL(uint32_t) PDMR3CritSectCountOwned(PVM pVM, char *pszNames, size_t cbNames)
473{
474 /*
475 * Init the name buffer.
476 */
477 size_t cchLeft = cbNames;
478 if (cchLeft)
479 {
480 cchLeft--;
481 pszNames[0] = pszNames[cchLeft] = '\0';
482 }
483
484 /*
485 * Iterate the critical sections.
486 */
487 /* This is unsafe, but wtf. */
488 RTNATIVETHREAD const hNativeThread = RTThreadNativeSelf();
489 uint32_t cCritSects = 0;
490 for (PPDMCRITSECTINT pCur = pVM->pdm.s.pCritSects;
491 pCur;
492 pCur = pCur->pNext)
493 {
494 /* Same as RTCritSectIsOwner(). */
495 if (pCur->Core.NativeThreadOwner == hNativeThread)
496 {
497 cCritSects++;
498
499 /*
500 * Copy the name if there is space. Fun stuff.
501 */
502 if (cchLeft)
503 {
504 /* try add comma. */
505 if (cCritSects != 1)
506 {
507 *pszNames++ = ',';
508 if (--cchLeft)
509 {
510 *pszNames++ = ' ';
511 cchLeft--;
512 }
513 }
514
515 /* try copy the name. */
516 if (cchLeft)
517 {
518 size_t const cchName = strlen(pCur->pszName);
519 if (cchName < cchLeft)
520 {
521 memcpy(pszNames, pCur->pszName, cchName);
522 pszNames += cchName;
523 cchLeft -= cchName;
524 }
525 else
526 {
527 if (cchLeft > 2)
528 {
529 memcpy(pszNames, pCur->pszName, cchLeft - 2);
530 pszNames += cchLeft - 2;
531 cchLeft = 2;
532 }
533 while (cchLeft-- > 0)
534 *pszNames++ = '+';
535 }
536 }
537 *pszNames = '\0';
538 }
539 }
540 }
541
542 return cCritSects;
543}
544
545
546/**
547 * Leave all critical sections the calling thread owns.
548 *
549 * @param pVM The VM handle.
550 */
551void PDMR3CritSectLeaveAll(PVM pVM)
552{
553 RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
554
555 RTCritSectEnter(&pVM->pdm.s.MiscCritSect);
556 for (PPDMCRITSECTINT pCur = pVM->pdm.s.pCritSects;
557 pCur;
558 pCur = pCur->pNext)
559 {
560 while ( pCur->Core.NativeThreadOwner == hNativeSelf
561 && pCur->Core.cNestings > 0)
562 PDMCritSectLeave((PPDMCRITSECT)pCur);
563 }
564 RTCritSectLeave(&pVM->pdm.s.MiscCritSect);
565}
566
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use