VirtualBox

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

Last change on this file since 7729 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette