VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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