VirtualBox

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

Last change on this file since 50653 was 44529, checked in by vboxsync, 11 years ago

header (C) fixes

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

© 2023 Oracle
ContactPrivacy policyTerms of Use