VirtualBox

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

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

VMM: Added PDMCritSectHasWaiters and PDMR3CritSectYield.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use