VirtualBox

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

Last change on this file since 28800 was 28800, checked in by vboxsync, 14 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 34.0 KB
Line 
1/* $Id: PDMThread.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */
2/** @file
3 * PDM Thread - VM Thread Management.
4 */
5
6/*
7 * Copyright (C) 2007 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/pdm.h>
24#include <VBox/mm.h>
25#include <VBox/vm.h>
26#include <VBox/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_INTERNAL_ERROR;
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 The VM handle.
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 The VM handle.
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_INTERNAL_ERROR;
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 The VM handle.
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 The VM handle.
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 The VM handle.
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 The VM handle.
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 The VM handle.
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_INTERNAL_ERROR;
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 The VM handle.
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 The VM handle.
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 The VM handle.
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 The VM handle.
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_INTERNAL_ERROR;
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_INTERNAL_ERROR);
735 AssertReturn(pThread->Thread == RTThreadSelf(), VERR_INTERNAL_ERROR);
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 /*
762 * The run loop.
763 *
764 * It handles simple thread functions which returns when they see a suspending
765 * request and leaves the PDMR3ThreadIAmSuspending and PDMR3ThreadIAmRunning
766 * parts to us.
767 */
768 int rc;
769 for (;;)
770 {
771 switch (pThread->Internal.s.enmType)
772 {
773 case PDMTHREADTYPE_DEVICE:
774 rc = pThread->u.Dev.pfnThread(pThread->u.Dev.pDevIns, pThread);
775 break;
776
777 case PDMTHREADTYPE_USB:
778 rc = pThread->u.Usb.pfnThread(pThread->u.Usb.pUsbIns, pThread);
779 break;
780
781 case PDMTHREADTYPE_DRIVER:
782 rc = pThread->u.Drv.pfnThread(pThread->u.Drv.pDrvIns, pThread);
783 break;
784
785 case PDMTHREADTYPE_INTERNAL:
786 rc = pThread->u.Int.pfnThread(pThread->Internal.s.pVM, pThread);
787 break;
788
789 case PDMTHREADTYPE_EXTERNAL:
790 rc = pThread->u.Ext.pfnThread(pThread);
791 break;
792
793 default:
794 AssertMsgFailed(("%d\n", pThread->Internal.s.enmType));
795 rc = VERR_INTERNAL_ERROR;
796 break;
797 }
798 if (RT_FAILURE(rc))
799 break;
800
801 /*
802 * If this is a simple thread function, the state will be suspending
803 * or initializing now. If it isn't we're supposed to terminate.
804 */
805 if ( pThread->enmState != PDMTHREADSTATE_SUSPENDING
806 && pThread->enmState != PDMTHREADSTATE_INITIALIZING)
807 {
808 Assert(pThread->enmState == PDMTHREADSTATE_TERMINATING);
809 break;
810 }
811 rc = PDMR3ThreadIAmSuspending(pThread);
812 if (RT_FAILURE(rc))
813 break;
814 if (pThread->enmState != PDMTHREADSTATE_RESUMING)
815 {
816 Assert(pThread->enmState == PDMTHREADSTATE_TERMINATING);
817 break;
818 }
819
820 rc = PDMR3ThreadIAmRunning(pThread);
821 if (RT_FAILURE(rc))
822 break;
823 }
824
825 if (RT_FAILURE(rc))
826 LogRel(("PDMThread: Thread '%s' (%RTthrd) quit unexpectedly with rc=%Rrc.\n", RTThreadGetName(Thread), Thread, rc));
827
828 /*
829 * Advance the state to terminating and then on to terminated.
830 */
831 for (;;)
832 {
833 PDMTHREADSTATE enmState = pThread->enmState;
834 if ( enmState == PDMTHREADSTATE_TERMINATING
835 || pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
836 break;
837 }
838
839 ASMAtomicXchgSize(&pThread->enmState, PDMTHREADSTATE_TERMINATED);
840 int rc2 = RTThreadUserSignal(Thread); AssertRC(rc2);
841
842 Log(("PDMThread: Terminating thread %RTthrd / %p / '%s': %Rrc\n", Thread, pThread, RTThreadGetName(Thread), rc));
843 return rc;
844}
845
846
847/**
848 * Initiate termination of the thread because something failed in a bad way.
849 *
850 * @param pThread The PDM thread.
851 */
852static void pdmR3ThreadBailOut(PPDMTHREAD pThread)
853{
854 for (;;)
855 {
856 PDMTHREADSTATE enmState = pThread->enmState;
857 switch (enmState)
858 {
859 case PDMTHREADSTATE_SUSPENDING:
860 case PDMTHREADSTATE_SUSPENDED:
861 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
862 continue;
863 RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
864 break;
865
866 case PDMTHREADSTATE_RESUMING:
867 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
868 continue;
869 break;
870
871 case PDMTHREADSTATE_RUNNING:
872 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
873 continue;
874 pdmR3ThreadWakeUp(pThread);
875 break;
876
877 case PDMTHREADSTATE_TERMINATING:
878 case PDMTHREADSTATE_TERMINATED:
879 break;
880
881 case PDMTHREADSTATE_INITIALIZING:
882 default:
883 AssertMsgFailed(("enmState=%d\n", enmState));
884 break;
885 }
886 break;
887 }
888}
889
890
891/**
892 * Suspends the thread.
893 *
894 * This can be called at the power off / suspend notifications to suspend the
895 * PDM thread a bit early. The thread will be automatically suspend upon
896 * completion of the device/driver notification cycle.
897 *
898 * The caller is responsible for serializing the control operations on the
899 * thread. That basically means, always do these calls from the EMT.
900 *
901 * @returns VBox status code.
902 * @param pThread The PDM thread.
903 */
904VMMR3DECL(int) PDMR3ThreadSuspend(PPDMTHREAD pThread)
905{
906 /*
907 * Assert sanity.
908 */
909 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
910 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
911 Assert(pThread->Thread != RTThreadSelf());
912
913 /*
914 * This is a noop if the thread is already suspended.
915 */
916 if (pThread->enmState == PDMTHREADSTATE_SUSPENDED)
917 return VINF_SUCCESS;
918
919 /*
920 * Change the state to resuming and kick the thread.
921 */
922 int rc = RTSemEventMultiReset(pThread->Internal.s.BlockEvent);
923 if (RT_SUCCESS(rc))
924 {
925 rc = RTThreadUserReset(pThread->Thread);
926 if (RT_SUCCESS(rc))
927 {
928 rc = VERR_WRONG_ORDER;
929 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_SUSPENDING, PDMTHREADSTATE_RUNNING))
930 {
931 rc = pdmR3ThreadWakeUp(pThread);
932 if (RT_SUCCESS(rc))
933 {
934 /*
935 * Wait for the thread to reach the suspended state.
936 */
937 if (pThread->enmState != PDMTHREADSTATE_SUSPENDED)
938 rc = RTThreadUserWait(pThread->Thread, 60*1000);
939 if ( RT_SUCCESS(rc)
940 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
941 rc = VERR_INTERNAL_ERROR;
942 if (RT_SUCCESS(rc))
943 return rc;
944 }
945 }
946 }
947 }
948
949 /*
950 * Something failed, initialize termination.
951 */
952 AssertMsgFailed(("PDMR3ThreadSuspend -> rc=%Rrc enmState=%d\n", rc, pThread->enmState));
953 pdmR3ThreadBailOut(pThread);
954 return rc;
955}
956
957
958/**
959 * Suspend all running threads.
960 *
961 * This is called by PDMR3Suspend() and PDMR3PowerOff() after all the devices
962 * and drivers have been notified about the suspend / power off.
963 *
964 * @return VBox status code.
965 * @param pVM The VM handle.
966 */
967int pdmR3ThreadSuspendAll(PVM pVM)
968{
969 PUVM pUVM = pVM->pUVM;
970 RTCritSectEnter(&pUVM->pdm.s.ListCritSect); /* This may cause deadlocks later... */
971 for (PPDMTHREAD pThread = pUVM->pdm.s.pThreads; pThread; pThread = pThread->Internal.s.pNext)
972 switch (pThread->enmState)
973 {
974 case PDMTHREADSTATE_RUNNING:
975 {
976 int rc = PDMR3ThreadSuspend(pThread);
977 AssertRCReturn(rc, rc);
978 break;
979 }
980
981 /* suspend -> power off; voluntary suspend. */
982 case PDMTHREADSTATE_SUSPENDED:
983 break;
984
985 default:
986 AssertMsgFailed(("pThread=%p enmState=%d\n", pThread, pThread->enmState));
987 break;
988 }
989 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
990 return VINF_SUCCESS;
991}
992
993
994/**
995 * Resumes the thread.
996 *
997 * This can be called the power on / resume notifications to resume the
998 * PDM thread a bit early. The thread will be automatically resumed upon
999 * return from these two notification callbacks (devices/drivers).
1000 *
1001 * The caller is responsible for serializing the control operations on the
1002 * thread. That basically means, always do these calls from the EMT.
1003 *
1004 * @returns VBox status code.
1005 * @param pThread The PDM thread.
1006 */
1007VMMR3DECL(int) PDMR3ThreadResume(PPDMTHREAD pThread)
1008{
1009 /*
1010 * Assert sanity.
1011 */
1012 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
1013 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
1014 Assert(pThread->Thread != RTThreadSelf());
1015
1016 /*
1017 * Change the state to resuming and kick the thread.
1018 */
1019 int rc = RTThreadUserReset(pThread->Thread);
1020 if (RT_SUCCESS(rc))
1021 {
1022 rc = VERR_WRONG_ORDER;
1023 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_RESUMING, PDMTHREADSTATE_SUSPENDED))
1024 {
1025 rc = RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
1026 if (RT_SUCCESS(rc))
1027 {
1028 /*
1029 * Wait for the thread to reach the running state.
1030 */
1031 rc = RTThreadUserWait(pThread->Thread, 60*1000);
1032 if ( RT_SUCCESS(rc)
1033 && pThread->enmState != PDMTHREADSTATE_RUNNING)
1034 rc = VERR_INTERNAL_ERROR;
1035 if (RT_SUCCESS(rc))
1036 return rc;
1037 }
1038 }
1039 }
1040
1041 /*
1042 * Something failed, initialize termination.
1043 */
1044 AssertMsgFailed(("PDMR3ThreadResume -> rc=%Rrc enmState=%d\n", rc, pThread->enmState));
1045 pdmR3ThreadBailOut(pThread);
1046 return rc;
1047}
1048
1049
1050/**
1051 * Resumes all threads not running.
1052 *
1053 * This is called by PDMR3Resume() and PDMR3PowerOn() after all the devices
1054 * and drivers have been notified about the resume / power on .
1055 *
1056 * @return VBox status code.
1057 * @param pVM The VM handle.
1058 */
1059int pdmR3ThreadResumeAll(PVM pVM)
1060{
1061 PUVM pUVM = pVM->pUVM;
1062 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
1063 for (PPDMTHREAD pThread = pUVM->pdm.s.pThreads; pThread; pThread = pThread->Internal.s.pNext)
1064 switch (pThread->enmState)
1065 {
1066 case PDMTHREADSTATE_SUSPENDED:
1067 {
1068 int rc = PDMR3ThreadResume(pThread);
1069 AssertRCReturn(rc, rc);
1070 break;
1071 }
1072
1073 default:
1074 AssertMsgFailed(("pThread=%p enmState=%d\n", pThread, pThread->enmState));
1075 break;
1076 }
1077 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1078 return VINF_SUCCESS;
1079}
1080
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use