VirtualBox

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

Last change on this file since 28265 was 28265, checked in by vboxsync, 15 years ago

PDMThread: serialize list manipulations using ListCritSect.

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