VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PDMThread.cpp@ 74795

Last change on this file since 74795 was 69175, checked in by vboxsync, 7 years ago

VMM/PDMThread: Don't use MMHyperFree but MMR3HeapFree instead, that structure doesn't live in the hyper heap

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 35.0 KB
Line 
1/* $Id: PDMThread.cpp 69175 2017-10-23 18:01:55Z vboxsync $ */
2/** @file
3 * PDM Thread - VM Thread Management.
4 */
5
6/*
7 * Copyright (C) 2007-2017 Oracle Corporation
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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22/// @todo \#define LOG_GROUP LOG_GROUP_PDM_THREAD
23#include "PDMInternal.h"
24#include <VBox/vmm/pdm.h>
25#include <VBox/vmm/mm.h>
26#include <VBox/vmm/vm.h>
27#include <VBox/vmm/uvm.h>
28#include <VBox/err.h>
29
30#include <VBox/log.h>
31#include <iprt/asm.h>
32#include <iprt/semaphore.h>
33#include <iprt/assert.h>
34#include <iprt/thread.h>
35
36
37/*********************************************************************************************************************************
38* Internal Functions *
39*********************************************************************************************************************************/
40static DECLCALLBACK(int) pdmR3ThreadMain(RTTHREAD Thread, void *pvUser);
41
42
43/**
44 * Wrapper around ASMAtomicCmpXchgSize.
45 */
46DECLINLINE(bool) pdmR3AtomicCmpXchgState(PPDMTHREAD pThread, PDMTHREADSTATE enmNewState, PDMTHREADSTATE enmOldState)
47{
48 bool fRc;
49 ASMAtomicCmpXchgSize(&pThread->enmState, enmNewState, enmOldState, fRc);
50 return fRc;
51}
52
53
54/**
55 * Does the wakeup call.
56 *
57 * @returns VBox status code. Already asserted on failure.
58 * @param pThread The PDM thread.
59 */
60static DECLCALLBACK(int) pdmR3ThreadWakeUp(PPDMTHREAD pThread)
61{
62 RTSemEventMultiSignal(pThread->Internal.s.SleepEvent);
63
64 int rc;
65 switch (pThread->Internal.s.enmType)
66 {
67 case PDMTHREADTYPE_DEVICE:
68 rc = pThread->u.Dev.pfnWakeUp(pThread->u.Dev.pDevIns, pThread);
69 break;
70
71 case PDMTHREADTYPE_USB:
72 rc = pThread->u.Usb.pfnWakeUp(pThread->u.Usb.pUsbIns, pThread);
73 break;
74
75 case PDMTHREADTYPE_DRIVER:
76 rc = pThread->u.Drv.pfnWakeUp(pThread->u.Drv.pDrvIns, pThread);
77 break;
78
79 case PDMTHREADTYPE_INTERNAL:
80 rc = pThread->u.Int.pfnWakeUp(pThread->Internal.s.pVM, pThread);
81 break;
82
83 case PDMTHREADTYPE_EXTERNAL:
84 rc = pThread->u.Ext.pfnWakeUp(pThread);
85 break;
86
87 default:
88 AssertMsgFailed(("%d\n", pThread->Internal.s.enmType));
89 rc = VERR_PDM_THREAD_IPE_1;
90 break;
91 }
92 AssertRC(rc);
93 return rc;
94}
95
96
97/**
98 * Allocates new thread instance.
99 *
100 * @returns VBox status code.
101 * @param pVM The cross context VM structure.
102 * @param ppThread Where to store the pointer to the instance.
103 */
104static int pdmR3ThreadNew(PVM pVM, PPPDMTHREAD ppThread)
105{
106 PPDMTHREAD pThread;
107 int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_THREAD, sizeof(*pThread), (void **)&pThread);
108 if (RT_FAILURE(rc))
109 return rc;
110
111 pThread->u32Version = PDMTHREAD_VERSION;
112 pThread->enmState = PDMTHREADSTATE_INITIALIZING;
113 pThread->Thread = NIL_RTTHREAD;
114 pThread->Internal.s.pVM = pVM;
115
116 *ppThread = pThread;
117 return VINF_SUCCESS;
118}
119
120
121
122/**
123 * Initialize a new thread, this actually creates the thread.
124 *
125 * @returns VBox status code.
126 * @param pVM The cross context VM structure.
127 * @param ppThread Where the thread instance data handle is.
128 * @param cbStack The stack size, see RTThreadCreate().
129 * @param enmType The thread type, see RTThreadCreate().
130 * @param pszName The thread name, see RTThreadCreate().
131 */
132static int pdmR3ThreadInit(PVM pVM, PPPDMTHREAD ppThread, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
133{
134 PPDMTHREAD pThread = *ppThread;
135 PUVM pUVM = pVM->pUVM;
136
137 /*
138 * Initialize the remainder of the structure.
139 */
140 pThread->Internal.s.pVM = pVM;
141
142 int rc = RTSemEventMultiCreate(&pThread->Internal.s.BlockEvent);
143 if (RT_SUCCESS(rc))
144 {
145 rc = RTSemEventMultiCreate(&pThread->Internal.s.SleepEvent);
146 if (RT_SUCCESS(rc))
147 {
148 /*
149 * Create the thread and wait for it to initialize.
150 * The newly created thread will set the PDMTHREAD::Thread member.
151 */
152 RTTHREAD Thread;
153 rc = RTThreadCreate(&Thread, pdmR3ThreadMain, pThread, cbStack, enmType, RTTHREADFLAGS_WAITABLE, pszName);
154 if (RT_SUCCESS(rc))
155 {
156 rc = RTThreadUserWait(Thread, 60*1000);
157 if ( RT_SUCCESS(rc)
158 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
159 rc = VERR_PDM_THREAD_IPE_2;
160 if (RT_SUCCESS(rc))
161 {
162 /*
163 * Insert it into the thread list.
164 */
165 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
166 pThread->Internal.s.pNext = NULL;
167 if (pUVM->pdm.s.pThreadsTail)
168 pUVM->pdm.s.pThreadsTail->Internal.s.pNext = pThread;
169 else
170 pUVM->pdm.s.pThreads = pThread;
171 pUVM->pdm.s.pThreadsTail = pThread;
172 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
173
174 rc = RTThreadUserReset(Thread);
175 AssertRC(rc);
176 return rc;
177 }
178
179 /* bailout */
180 RTThreadWait(Thread, 60*1000, NULL);
181 }
182 RTSemEventMultiDestroy(pThread->Internal.s.SleepEvent);
183 pThread->Internal.s.SleepEvent = NIL_RTSEMEVENTMULTI;
184 }
185 RTSemEventMultiDestroy(pThread->Internal.s.BlockEvent);
186 pThread->Internal.s.BlockEvent = NIL_RTSEMEVENTMULTI;
187 }
188 MMR3HeapFree(pThread);
189 *ppThread = NULL;
190
191 return rc;
192}
193
194
195/**
196 * Device Helper for creating a thread associated with a device.
197 *
198 * @returns VBox status code.
199 * @param pVM The cross context VM structure.
200 * @param pDevIns The device instance.
201 * @param ppThread Where to store the thread 'handle'.
202 * @param pvUser The user argument to the thread function.
203 * @param pfnThread The thread function.
204 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
205 * a state change is pending.
206 * @param cbStack See RTThreadCreate.
207 * @param enmType See RTThreadCreate.
208 * @param pszName See RTThreadCreate.
209 */
210int pdmR3ThreadCreateDevice(PVM pVM, PPDMDEVINS pDevIns, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADDEV pfnThread,
211 PFNPDMTHREADWAKEUPDEV pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
212{
213 int rc = pdmR3ThreadNew(pVM, ppThread);
214 if (RT_SUCCESS(rc))
215 {
216 PPDMTHREAD pThread = *ppThread;
217 pThread->pvUser = pvUser;
218 pThread->Internal.s.enmType = PDMTHREADTYPE_DEVICE;
219 pThread->u.Dev.pDevIns = pDevIns;
220 pThread->u.Dev.pfnThread = pfnThread;
221 pThread->u.Dev.pfnWakeUp = pfnWakeUp;
222 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
223 }
224 return rc;
225}
226
227
228/**
229 * USB Device Helper for creating a thread associated with an USB device.
230 *
231 * @returns VBox status code.
232 * @param pVM The cross context VM structure.
233 * @param pUsbIns The USB device instance.
234 * @param ppThread Where to store the thread 'handle'.
235 * @param pvUser The user argument to the thread function.
236 * @param pfnThread The thread function.
237 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
238 * a state change is pending.
239 * @param cbStack See RTThreadCreate.
240 * @param enmType See RTThreadCreate.
241 * @param pszName See RTThreadCreate.
242 */
243int pdmR3ThreadCreateUsb(PVM pVM, PPDMUSBINS pUsbIns, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADUSB pfnThread,
244 PFNPDMTHREADWAKEUPUSB pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
245{
246 int rc = pdmR3ThreadNew(pVM, ppThread);
247 if (RT_SUCCESS(rc))
248 {
249 PPDMTHREAD pThread = *ppThread;
250 pThread->pvUser = pvUser;
251 pThread->Internal.s.enmType = PDMTHREADTYPE_USB;
252 pThread->u.Usb.pUsbIns = pUsbIns;
253 pThread->u.Usb.pfnThread = pfnThread;
254 pThread->u.Usb.pfnWakeUp = pfnWakeUp;
255 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
256 }
257 return rc;
258}
259
260
261/**
262 * Driver Helper for creating a thread associated with a driver.
263 *
264 * @returns VBox status code.
265 * @param pVM The cross context VM structure.
266 * @param pDrvIns The driver instance.
267 * @param ppThread Where to store the thread 'handle'.
268 * @param pvUser The user argument to the thread function.
269 * @param pfnThread The thread function.
270 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
271 * a state change is pending.
272 * @param cbStack See RTThreadCreate.
273 * @param enmType See RTThreadCreate.
274 * @param pszName See RTThreadCreate.
275 */
276int pdmR3ThreadCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADDRV pfnThread,
277 PFNPDMTHREADWAKEUPDRV pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
278{
279 int rc = pdmR3ThreadNew(pVM, ppThread);
280 if (RT_SUCCESS(rc))
281 {
282 PPDMTHREAD pThread = *ppThread;
283 pThread->pvUser = pvUser;
284 pThread->Internal.s.enmType = PDMTHREADTYPE_DRIVER;
285 pThread->u.Drv.pDrvIns = pDrvIns;
286 pThread->u.Drv.pfnThread = pfnThread;
287 pThread->u.Drv.pfnWakeUp = pfnWakeUp;
288 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
289 }
290 return rc;
291}
292
293
294/**
295 * Creates a PDM thread for internal use in the VM.
296 *
297 * @returns VBox status code.
298 * @param pVM The cross context VM structure.
299 * @param ppThread Where to store the thread 'handle'.
300 * @param pvUser The user argument to the thread function.
301 * @param pfnThread The thread function.
302 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
303 * a state change is pending.
304 * @param cbStack See RTThreadCreate.
305 * @param enmType See RTThreadCreate.
306 * @param pszName See RTThreadCreate.
307 */
308VMMR3DECL(int) PDMR3ThreadCreate(PVM pVM, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADINT pfnThread,
309 PFNPDMTHREADWAKEUPINT pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
310{
311 int rc = pdmR3ThreadNew(pVM, ppThread);
312 if (RT_SUCCESS(rc))
313 {
314 PPDMTHREAD pThread = *ppThread;
315 pThread->pvUser = pvUser;
316 pThread->Internal.s.enmType = PDMTHREADTYPE_INTERNAL;
317 pThread->u.Int.pfnThread = pfnThread;
318 pThread->u.Int.pfnWakeUp = pfnWakeUp;
319 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
320 }
321 return rc;
322}
323
324
325/**
326 * Creates a PDM thread for VM use by some external party.
327 *
328 * @returns VBox status code.
329 * @param pVM The cross context VM structure.
330 * @param ppThread Where to store the thread 'handle'.
331 * @param pvUser The user argument to the thread function.
332 * @param pfnThread The thread function.
333 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
334 * a state change is pending.
335 * @param cbStack See RTThreadCreate.
336 * @param enmType See RTThreadCreate.
337 * @param pszName See RTThreadCreate.
338 */
339VMMR3DECL(int) PDMR3ThreadCreateExternal(PVM pVM, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADEXT pfnThread,
340 PFNPDMTHREADWAKEUPEXT pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
341{
342 int rc = pdmR3ThreadNew(pVM, ppThread);
343 if (RT_SUCCESS(rc))
344 {
345 PPDMTHREAD pThread = *ppThread;
346 pThread->pvUser = pvUser;
347 pThread->Internal.s.enmType = PDMTHREADTYPE_EXTERNAL;
348 pThread->u.Ext.pfnThread = pfnThread;
349 pThread->u.Ext.pfnWakeUp = pfnWakeUp;
350 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
351 }
352 return rc;
353}
354
355
356/**
357 * Destroys a PDM thread.
358 *
359 * This will wakeup the thread, tell it to terminate, and wait for it terminate.
360 *
361 * @returns VBox status code.
362 * This reflects the success off destroying the thread and not the exit code
363 * of the thread as this is stored in *pRcThread.
364 * @param pThread The thread to destroy.
365 * @param pRcThread Where to store the thread exit code. Optional.
366 * @thread The emulation thread (EMT).
367 */
368VMMR3DECL(int) PDMR3ThreadDestroy(PPDMTHREAD pThread, int *pRcThread)
369{
370 /*
371 * Assert sanity.
372 */
373 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
374 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
375 Assert(pThread->Thread != RTThreadSelf());
376 AssertPtrNullReturn(pRcThread, VERR_INVALID_POINTER);
377 PVM pVM = pThread->Internal.s.pVM;
378 VM_ASSERT_EMT(pVM);
379 PUVM pUVM = pVM->pUVM;
380
381 /*
382 * Advance the thread to the terminating state.
383 */
384 int rc = VINF_SUCCESS;
385 if (pThread->enmState <= PDMTHREADSTATE_TERMINATING)
386 {
387 for (;;)
388 {
389 PDMTHREADSTATE enmState = pThread->enmState;
390 switch (enmState)
391 {
392 case PDMTHREADSTATE_RUNNING:
393 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
394 continue;
395 rc = pdmR3ThreadWakeUp(pThread);
396 break;
397
398 case PDMTHREADSTATE_SUSPENDED:
399 case PDMTHREADSTATE_SUSPENDING:
400 case PDMTHREADSTATE_RESUMING:
401 case PDMTHREADSTATE_INITIALIZING:
402 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
403 continue;
404 break;
405
406 case PDMTHREADSTATE_TERMINATING:
407 case PDMTHREADSTATE_TERMINATED:
408 break;
409
410 default:
411 AssertMsgFailed(("enmState=%d\n", enmState));
412 rc = VERR_PDM_THREAD_IPE_2;
413 break;
414 }
415 break;
416 }
417 }
418 int rc2 = RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
419 AssertRC(rc2);
420
421 /*
422 * Wait for it to terminate and the do cleanups.
423 */
424 rc2 = RTThreadWait(pThread->Thread, RT_SUCCESS(rc) ? 60*1000 : 150, pRcThread);
425 if (RT_SUCCESS(rc2))
426 {
427 /* make it invalid. */
428 pThread->u32Version = 0xffffffff;
429 pThread->enmState = PDMTHREADSTATE_INVALID;
430 pThread->Thread = NIL_RTTHREAD;
431
432 /* unlink */
433 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
434 if (pUVM->pdm.s.pThreads == pThread)
435 {
436 pUVM->pdm.s.pThreads = pThread->Internal.s.pNext;
437 if (!pThread->Internal.s.pNext)
438 pUVM->pdm.s.pThreadsTail = NULL;
439 }
440 else
441 {
442 PPDMTHREAD pPrev = pUVM->pdm.s.pThreads;
443 while (pPrev && pPrev->Internal.s.pNext != pThread)
444 pPrev = pPrev->Internal.s.pNext;
445 Assert(pPrev);
446 if (pPrev)
447 pPrev->Internal.s.pNext = pThread->Internal.s.pNext;
448 if (!pThread->Internal.s.pNext)
449 pUVM->pdm.s.pThreadsTail = pPrev;
450 }
451 pThread->Internal.s.pNext = NULL;
452 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
453
454 /* free the resources */
455 RTSemEventMultiDestroy(pThread->Internal.s.BlockEvent);
456 pThread->Internal.s.BlockEvent = NIL_RTSEMEVENTMULTI;
457
458 RTSemEventMultiDestroy(pThread->Internal.s.SleepEvent);
459 pThread->Internal.s.SleepEvent = NIL_RTSEMEVENTMULTI;
460
461 MMR3HeapFree(pThread);
462 }
463 else if (RT_SUCCESS(rc))
464 rc = rc2;
465
466 return rc;
467}
468
469
470/**
471 * Destroys all threads associated with a device.
472 *
473 * This function is called by PDMDevice when a device is
474 * destroyed (not currently implemented).
475 *
476 * @returns VBox status code of the first failure.
477 * @param pVM The cross context VM structure.
478 * @param pDevIns the device instance.
479 */
480int pdmR3ThreadDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
481{
482 int rc = VINF_SUCCESS;
483 PUVM pUVM = pVM->pUVM;
484
485 AssertPtr(pDevIns);
486
487 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
488 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
489 while (pThread)
490 {
491 PPDMTHREAD pNext = pThread->Internal.s.pNext;
492 if ( pThread->Internal.s.enmType == PDMTHREADTYPE_DEVICE
493 && pThread->u.Dev.pDevIns == pDevIns)
494 {
495 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
496 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
497 rc = rc2;
498 }
499 pThread = pNext;
500 }
501 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
502 return rc;
503}
504
505
506/**
507 * Destroys all threads associated with an USB device.
508 *
509 * This function is called by PDMUsb when a device is destroyed.
510 *
511 * @returns VBox status code of the first failure.
512 * @param pVM The cross context VM structure.
513 * @param pUsbIns The USB device instance.
514 */
515int pdmR3ThreadDestroyUsb(PVM pVM, PPDMUSBINS pUsbIns)
516{
517 int rc = VINF_SUCCESS;
518 PUVM pUVM = pVM->pUVM;
519
520 AssertPtr(pUsbIns);
521
522 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
523 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
524 while (pThread)
525 {
526 PPDMTHREAD pNext = pThread->Internal.s.pNext;
527 if ( pThread->Internal.s.enmType == PDMTHREADTYPE_DEVICE
528 && pThread->u.Usb.pUsbIns == pUsbIns)
529 {
530 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
531 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
532 rc = rc2;
533 }
534 pThread = pNext;
535 }
536 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
537 return rc;
538}
539
540
541/**
542 * Destroys all threads associated with a driver.
543 *
544 * This function is called by PDMDriver when a driver is destroyed.
545 *
546 * @returns VBox status code of the first failure.
547 * @param pVM The cross context VM structure.
548 * @param pDrvIns The driver instance.
549 */
550int pdmR3ThreadDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
551{
552 int rc = VINF_SUCCESS;
553 PUVM pUVM = pVM->pUVM;
554
555 AssertPtr(pDrvIns);
556
557 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
558 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
559 while (pThread)
560 {
561 PPDMTHREAD pNext = pThread->Internal.s.pNext;
562 if ( pThread->Internal.s.enmType == PDMTHREADTYPE_DRIVER
563 && pThread->u.Drv.pDrvIns == pDrvIns)
564 {
565 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
566 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
567 rc = rc2;
568 }
569 pThread = pNext;
570 }
571 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
572 return rc;
573}
574
575
576/**
577 * Called For VM power off.
578 *
579 * @param pVM The cross context VM structure.
580 */
581void pdmR3ThreadDestroyAll(PVM pVM)
582{
583 PUVM pUVM = pVM->pUVM;
584 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
585 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
586 while (pThread)
587 {
588 PPDMTHREAD pNext = pThread->Internal.s.pNext;
589 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
590 AssertRC(rc2);
591 pThread = pNext;
592 }
593 Assert(!pUVM->pdm.s.pThreads && !pUVM->pdm.s.pThreadsTail);
594 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
595}
596
597
598/**
599 * Initiate termination of the thread (self) because something failed in a bad way.
600 *
601 * @param pThread The PDM thread.
602 */
603static void pdmR3ThreadBailMeOut(PPDMTHREAD pThread)
604{
605 for (;;)
606 {
607 PDMTHREADSTATE enmState = pThread->enmState;
608 switch (enmState)
609 {
610 case PDMTHREADSTATE_SUSPENDING:
611 case PDMTHREADSTATE_SUSPENDED:
612 case PDMTHREADSTATE_RESUMING:
613 case PDMTHREADSTATE_RUNNING:
614 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
615 continue;
616 break;
617
618 case PDMTHREADSTATE_TERMINATING:
619 case PDMTHREADSTATE_TERMINATED:
620 break;
621
622 case PDMTHREADSTATE_INITIALIZING:
623 default:
624 AssertMsgFailed(("enmState=%d\n", enmState));
625 break;
626 }
627 break;
628 }
629}
630
631
632/**
633 * Called by the PDM thread in response to a wakeup call with
634 * suspending as the new state.
635 *
636 * The thread will block in side this call until the state is changed in
637 * response to a VM state change or to the device/driver/whatever calling the
638 * PDMR3ThreadResume API.
639 *
640 * @returns VBox status code.
641 * On failure, terminate the thread.
642 * @param pThread The PDM thread.
643 */
644VMMR3DECL(int) PDMR3ThreadIAmSuspending(PPDMTHREAD pThread)
645{
646 /*
647 * Assert sanity.
648 */
649 AssertPtr(pThread);
650 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
651 Assert(pThread->Thread == RTThreadSelf() || pThread->enmState == PDMTHREADSTATE_INITIALIZING);
652 PDMTHREADSTATE enmState = pThread->enmState;
653 Assert( enmState == PDMTHREADSTATE_SUSPENDING
654 || enmState == PDMTHREADSTATE_INITIALIZING);
655
656 /*
657 * Update the state, notify the control thread (the API caller) and go to sleep.
658 */
659 int rc = VERR_WRONG_ORDER;
660 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_SUSPENDED, enmState))
661 {
662 rc = RTThreadUserSignal(pThread->Thread);
663 if (RT_SUCCESS(rc))
664 {
665 rc = RTSemEventMultiWait(pThread->Internal.s.BlockEvent, RT_INDEFINITE_WAIT);
666 if ( RT_SUCCESS(rc)
667 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
668 return rc;
669
670 if (RT_SUCCESS(rc))
671 rc = VERR_PDM_THREAD_IPE_2;
672 }
673 }
674
675 AssertMsgFailed(("rc=%d enmState=%d\n", rc, pThread->enmState));
676 pdmR3ThreadBailMeOut(pThread);
677 return rc;
678}
679
680
681/**
682 * Called by the PDM thread in response to a resuming state.
683 *
684 * The purpose of this API is to tell the PDMR3ThreadResume caller that
685 * the PDM thread has successfully resumed. It will also do the
686 * state transition from the resuming to the running state.
687 *
688 * @returns VBox status code.
689 * On failure, terminate the thread.
690 * @param pThread The PDM thread.
691 */
692VMMR3DECL(int) PDMR3ThreadIAmRunning(PPDMTHREAD pThread)
693{
694 /*
695 * Assert sanity.
696 */
697 Assert(pThread->enmState == PDMTHREADSTATE_RESUMING);
698 Assert(pThread->Thread == RTThreadSelf());
699
700 /*
701 * Update the state and tell the control thread (the guy calling the resume API).
702 */
703 int rc = VERR_WRONG_ORDER;
704 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_RUNNING, PDMTHREADSTATE_RESUMING))
705 {
706 rc = RTThreadUserSignal(pThread->Thread);
707 if (RT_SUCCESS(rc))
708 return rc;
709 }
710
711 AssertMsgFailed(("rc=%d enmState=%d\n", rc, pThread->enmState));
712 pdmR3ThreadBailMeOut(pThread);
713 return rc;
714}
715
716
717/**
718 * Called by the PDM thread instead of RTThreadSleep.
719 *
720 * The difference is that the sleep will be interrupted on state change. The
721 * thread must be in the running state, otherwise it will return immediately.
722 *
723 * @returns VBox status code.
724 * @retval VINF_SUCCESS on success or state change.
725 * @retval VERR_INTERRUPTED on signal or APC.
726 *
727 * @param pThread The PDM thread.
728 * @param cMillies The number of milliseconds to sleep.
729 */
730VMMR3DECL(int) PDMR3ThreadSleep(PPDMTHREAD pThread, RTMSINTERVAL cMillies)
731{
732 /*
733 * Assert sanity.
734 */
735 AssertReturn(pThread->enmState > PDMTHREADSTATE_INVALID && pThread->enmState < PDMTHREADSTATE_TERMINATED, VERR_PDM_THREAD_IPE_2);
736 AssertReturn(pThread->Thread == RTThreadSelf(), VERR_PDM_THREAD_INVALID_CALLER);
737
738 /*
739 * Reset the event semaphore, check the state and sleep.
740 */
741 RTSemEventMultiReset(pThread->Internal.s.SleepEvent);
742 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
743 return VINF_SUCCESS;
744 return RTSemEventMultiWaitNoResume(pThread->Internal.s.SleepEvent, cMillies);
745}
746
747
748/**
749 * The PDM thread function.
750 *
751 * @returns return from pfnThread.
752 *
753 * @param Thread The thread handle.
754 * @param pvUser Pointer to the PDMTHREAD structure.
755 */
756static DECLCALLBACK(int) pdmR3ThreadMain(RTTHREAD Thread, void *pvUser)
757{
758 PPDMTHREAD pThread = (PPDMTHREAD)pvUser;
759 Log(("PDMThread: Initializing thread %RTthrd / %p / '%s'...\n", Thread, pThread, RTThreadGetName(Thread)));
760 pThread->Thread = Thread;
761
762 PUVM pUVM = pThread->Internal.s.pVM->pUVM;
763 if ( pUVM->pVmm2UserMethods
764 && pUVM->pVmm2UserMethods->pfnNotifyPdmtInit)
765 pUVM->pVmm2UserMethods->pfnNotifyPdmtInit(pUVM->pVmm2UserMethods, pUVM);
766
767 /*
768 * The run loop.
769 *
770 * It handles simple thread functions which returns when they see a suspending
771 * request and leaves the PDMR3ThreadIAmSuspending and PDMR3ThreadIAmRunning
772 * parts to us.
773 */
774 int rc;
775 for (;;)
776 {
777 switch (pThread->Internal.s.enmType)
778 {
779 case PDMTHREADTYPE_DEVICE:
780 rc = pThread->u.Dev.pfnThread(pThread->u.Dev.pDevIns, pThread);
781 break;
782
783 case PDMTHREADTYPE_USB:
784 rc = pThread->u.Usb.pfnThread(pThread->u.Usb.pUsbIns, pThread);
785 break;
786
787 case PDMTHREADTYPE_DRIVER:
788 rc = pThread->u.Drv.pfnThread(pThread->u.Drv.pDrvIns, pThread);
789 break;
790
791 case PDMTHREADTYPE_INTERNAL:
792 rc = pThread->u.Int.pfnThread(pThread->Internal.s.pVM, pThread);
793 break;
794
795 case PDMTHREADTYPE_EXTERNAL:
796 rc = pThread->u.Ext.pfnThread(pThread);
797 break;
798
799 default:
800 AssertMsgFailed(("%d\n", pThread->Internal.s.enmType));
801 rc = VERR_PDM_THREAD_IPE_1;
802 break;
803 }
804 if (RT_FAILURE(rc))
805 break;
806
807 /*
808 * If this is a simple thread function, the state will be suspending
809 * or initializing now. If it isn't we're supposed to terminate.
810 */
811 if ( pThread->enmState != PDMTHREADSTATE_SUSPENDING
812 && pThread->enmState != PDMTHREADSTATE_INITIALIZING)
813 {
814 Assert(pThread->enmState == PDMTHREADSTATE_TERMINATING);
815 break;
816 }
817 rc = PDMR3ThreadIAmSuspending(pThread);
818 if (RT_FAILURE(rc))
819 break;
820 if (pThread->enmState != PDMTHREADSTATE_RESUMING)
821 {
822 Assert(pThread->enmState == PDMTHREADSTATE_TERMINATING);
823 break;
824 }
825
826 rc = PDMR3ThreadIAmRunning(pThread);
827 if (RT_FAILURE(rc))
828 break;
829 }
830
831 if (RT_FAILURE(rc))
832 LogRel(("PDMThread: Thread '%s' (%RTthrd) quit unexpectedly with rc=%Rrc.\n", RTThreadGetName(Thread), Thread, rc));
833
834 /*
835 * Advance the state to terminating and then on to terminated.
836 */
837 for (;;)
838 {
839 PDMTHREADSTATE enmState = pThread->enmState;
840 if ( enmState == PDMTHREADSTATE_TERMINATING
841 || pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
842 break;
843 }
844
845 ASMAtomicXchgSize(&pThread->enmState, PDMTHREADSTATE_TERMINATED);
846 int rc2 = RTThreadUserSignal(Thread); AssertRC(rc2);
847
848 if ( pUVM->pVmm2UserMethods
849 && pUVM->pVmm2UserMethods->pfnNotifyPdmtTerm)
850 pUVM->pVmm2UserMethods->pfnNotifyPdmtTerm(pUVM->pVmm2UserMethods, pUVM);
851 Log(("PDMThread: Terminating thread %RTthrd / %p / '%s': %Rrc\n", Thread, pThread, RTThreadGetName(Thread), rc));
852 return rc;
853}
854
855
856/**
857 * Initiate termination of the thread because something failed in a bad way.
858 *
859 * @param pThread The PDM thread.
860 */
861static void pdmR3ThreadBailOut(PPDMTHREAD pThread)
862{
863 for (;;)
864 {
865 PDMTHREADSTATE enmState = pThread->enmState;
866 switch (enmState)
867 {
868 case PDMTHREADSTATE_SUSPENDING:
869 case PDMTHREADSTATE_SUSPENDED:
870 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
871 continue;
872 RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
873 break;
874
875 case PDMTHREADSTATE_RESUMING:
876 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
877 continue;
878 break;
879
880 case PDMTHREADSTATE_RUNNING:
881 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
882 continue;
883 pdmR3ThreadWakeUp(pThread);
884 break;
885
886 case PDMTHREADSTATE_TERMINATING:
887 case PDMTHREADSTATE_TERMINATED:
888 break;
889
890 case PDMTHREADSTATE_INITIALIZING:
891 default:
892 AssertMsgFailed(("enmState=%d\n", enmState));
893 break;
894 }
895 break;
896 }
897}
898
899
900/**
901 * Suspends the thread.
902 *
903 * This can be called at the power off / suspend notifications to suspend the
904 * PDM thread a bit early. The thread will be automatically suspend upon
905 * completion of the device/driver notification cycle.
906 *
907 * The caller is responsible for serializing the control operations on the
908 * thread. That basically means, always do these calls from the EMT.
909 *
910 * @returns VBox status code.
911 * @param pThread The PDM thread.
912 */
913VMMR3DECL(int) PDMR3ThreadSuspend(PPDMTHREAD pThread)
914{
915 /*
916 * Assert sanity.
917 */
918 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
919 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
920 Assert(pThread->Thread != RTThreadSelf());
921
922 /*
923 * This is a noop if the thread is already suspended.
924 */
925 if (pThread->enmState == PDMTHREADSTATE_SUSPENDED)
926 return VINF_SUCCESS;
927
928 /*
929 * Change the state to resuming and kick the thread.
930 */
931 int rc = RTSemEventMultiReset(pThread->Internal.s.BlockEvent);
932 if (RT_SUCCESS(rc))
933 {
934 rc = RTThreadUserReset(pThread->Thread);
935 if (RT_SUCCESS(rc))
936 {
937 rc = VERR_WRONG_ORDER;
938 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_SUSPENDING, PDMTHREADSTATE_RUNNING))
939 {
940 rc = pdmR3ThreadWakeUp(pThread);
941 if (RT_SUCCESS(rc))
942 {
943 /*
944 * Wait for the thread to reach the suspended state.
945 */
946 if (pThread->enmState != PDMTHREADSTATE_SUSPENDED)
947 rc = RTThreadUserWait(pThread->Thread, 60*1000);
948 if ( RT_SUCCESS(rc)
949 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
950 rc = VERR_PDM_THREAD_IPE_2;
951 if (RT_SUCCESS(rc))
952 return rc;
953 }
954 }
955 }
956 }
957
958 /*
959 * Something failed, initialize termination.
960 */
961 AssertMsgFailed(("PDMR3ThreadSuspend -> rc=%Rrc enmState=%d suspending '%s'\n",
962 rc, pThread->enmState, RTThreadGetName(pThread->Thread)));
963 pdmR3ThreadBailOut(pThread);
964 return rc;
965}
966
967
968/**
969 * Suspend all running threads.
970 *
971 * This is called by PDMR3Suspend() and PDMR3PowerOff() after all the devices
972 * and drivers have been notified about the suspend / power off.
973 *
974 * @return VBox status code.
975 * @param pVM The cross context VM structure.
976 */
977int pdmR3ThreadSuspendAll(PVM pVM)
978{
979 PUVM pUVM = pVM->pUVM;
980 RTCritSectEnter(&pUVM->pdm.s.ListCritSect); /* This may cause deadlocks later... */
981 for (PPDMTHREAD pThread = pUVM->pdm.s.pThreads; pThread; pThread = pThread->Internal.s.pNext)
982 switch (pThread->enmState)
983 {
984 case PDMTHREADSTATE_RUNNING:
985 {
986 int rc = PDMR3ThreadSuspend(pThread);
987 AssertRCReturn(rc, rc);
988 break;
989 }
990
991 /* suspend -> power off; voluntary suspend. */
992 case PDMTHREADSTATE_SUSPENDED:
993 break;
994
995 default:
996 AssertMsgFailed(("pThread=%p enmState=%d\n", pThread, pThread->enmState));
997 break;
998 }
999 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1000 return VINF_SUCCESS;
1001}
1002
1003
1004/**
1005 * Resumes the thread.
1006 *
1007 * This can be called the power on / resume notifications to resume the
1008 * PDM thread a bit early. The thread will be automatically resumed upon
1009 * return from these two notification callbacks (devices/drivers).
1010 *
1011 * The caller is responsible for serializing the control operations on the
1012 * thread. That basically means, always do these calls from the EMT.
1013 *
1014 * @returns VBox status code.
1015 * @param pThread The PDM thread.
1016 */
1017VMMR3DECL(int) PDMR3ThreadResume(PPDMTHREAD pThread)
1018{
1019 /*
1020 * Assert sanity.
1021 */
1022 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
1023 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
1024 Assert(pThread->Thread != RTThreadSelf());
1025
1026 /*
1027 * Change the state to resuming and kick the thread.
1028 */
1029 int rc = RTThreadUserReset(pThread->Thread);
1030 if (RT_SUCCESS(rc))
1031 {
1032 rc = VERR_WRONG_ORDER;
1033 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_RESUMING, PDMTHREADSTATE_SUSPENDED))
1034 {
1035 rc = RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
1036 if (RT_SUCCESS(rc))
1037 {
1038 /*
1039 * Wait for the thread to reach the running state.
1040 */
1041 rc = RTThreadUserWait(pThread->Thread, 60*1000);
1042 if ( RT_SUCCESS(rc)
1043 && pThread->enmState != PDMTHREADSTATE_RUNNING)
1044 rc = VERR_PDM_THREAD_IPE_2;
1045 if (RT_SUCCESS(rc))
1046 return rc;
1047 }
1048 }
1049 }
1050
1051 /*
1052 * Something failed, initialize termination.
1053 */
1054 AssertMsgFailed(("PDMR3ThreadResume -> rc=%Rrc enmState=%d\n", rc, pThread->enmState));
1055 pdmR3ThreadBailOut(pThread);
1056 return rc;
1057}
1058
1059
1060/**
1061 * Resumes all threads not running.
1062 *
1063 * This is called by PDMR3Resume() and PDMR3PowerOn() after all the devices
1064 * and drivers have been notified about the resume / power on .
1065 *
1066 * @return VBox status code.
1067 * @param pVM The cross context VM structure.
1068 */
1069int pdmR3ThreadResumeAll(PVM pVM)
1070{
1071 PUVM pUVM = pVM->pUVM;
1072 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
1073 for (PPDMTHREAD pThread = pUVM->pdm.s.pThreads; pThread; pThread = pThread->Internal.s.pNext)
1074 switch (pThread->enmState)
1075 {
1076 case PDMTHREADSTATE_SUSPENDED:
1077 {
1078 int rc = PDMR3ThreadResume(pThread);
1079 AssertRCReturn(rc, rc);
1080 break;
1081 }
1082
1083 default:
1084 AssertMsgFailed(("pThread=%p enmState=%d\n", pThread, pThread->enmState));
1085 break;
1086 }
1087 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1088 return VINF_SUCCESS;
1089}
1090
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use