VirtualBox

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

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

PDMThread: New API PDMR3ThreadSleep. Fixed a per thread event sem leak.

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