VirtualBox

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

Last change on this file since 8155 was 8155, checked in by vboxsync, 16 years ago

The Big Sun Rebranding Header Change

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