VirtualBox

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

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

More PDM cleanup.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 32.8 KB
Line 
1/* $Id: PDMThread.cpp 12986 2008-10-04 23:41:17Z 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 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 */
307PDMR3DECL(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 */
338PDMR3DECL(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 */
367PDMR3DECL(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
379 /*
380 * Advance the thread to the terminating state.
381 */
382 int rc = VINF_SUCCESS;
383 if (pThread->enmState <= PDMTHREADSTATE_TERMINATING)
384 {
385 for (;;)
386 {
387 PDMTHREADSTATE enmState = pThread->enmState;
388 switch (enmState)
389 {
390 case PDMTHREADSTATE_RUNNING:
391 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
392 continue;
393 rc = pdmR3ThreadWakeUp(pThread);
394 break;
395
396 case PDMTHREADSTATE_SUSPENDED:
397 case PDMTHREADSTATE_SUSPENDING:
398 case PDMTHREADSTATE_RESUMING:
399 case PDMTHREADSTATE_INITIALIZING:
400 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
401 continue;
402 break;
403
404 case PDMTHREADSTATE_TERMINATING:
405 case PDMTHREADSTATE_TERMINATED:
406 break;
407
408 default:
409 AssertMsgFailed(("enmState=%d\n", enmState));
410 rc = VERR_INTERNAL_ERROR;
411 break;
412 }
413 break;
414 }
415 }
416 int rc2 = RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
417 AssertRC(rc2);
418
419 /*
420 * Wait for it to terminate and the do cleanups.
421 */
422 rc2 = RTThreadWait(pThread->Thread, RT_SUCCESS(rc) ? 60*1000 : 150, pRcThread);
423 if (RT_SUCCESS(rc2))
424 {
425 /* make it invalid. */
426 pThread->u32Version = 0xffffffff;
427 pThread->enmState = PDMTHREADSTATE_INVALID;
428 pThread->Thread = NIL_RTTHREAD;
429
430 /* unlink */
431 if (pVM->pdm.s.pThreads == pThread)
432 {
433 pVM->pdm.s.pThreads = pThread->Internal.s.pNext;
434 if (!pThread->Internal.s.pNext)
435 pVM->pdm.s.pThreadsTail = NULL;
436 }
437 else
438 {
439 PPDMTHREAD pPrev = pVM->pdm.s.pThreads;
440 while (pPrev && pPrev->Internal.s.pNext != pThread)
441 pPrev = pPrev->Internal.s.pNext;
442 Assert(pPrev);
443 if (pPrev)
444 pPrev->Internal.s.pNext = pThread->Internal.s.pNext;
445 if (!pThread->Internal.s.pNext)
446 pVM->pdm.s.pThreadsTail = pPrev;
447 }
448 pThread->Internal.s.pNext = NULL;
449
450 /* free the resources */
451 RTSemEventMultiDestroy(pThread->Internal.s.BlockEvent);
452 pThread->Internal.s.BlockEvent = NIL_RTSEMEVENTMULTI;
453
454 RTSemEventMultiDestroy(pThread->Internal.s.SleepEvent);
455 pThread->Internal.s.SleepEvent = NIL_RTSEMEVENTMULTI;
456
457 MMR3HeapFree(pThread);
458 }
459 else if (RT_SUCCESS(rc))
460 rc = rc2;
461
462 return rc;
463}
464
465
466/**
467 * Destroys all threads associated with a device.
468 *
469 * This function is called by PDMDevice when a device is
470 * destroyed (not currently implemented).
471 *
472 * @returns VBox status code of the first failure.
473 * @param pVM The VM handle.
474 * @param pDevIns the device instance.
475 */
476int pdmR3ThreadDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
477{
478 int rc = VINF_SUCCESS;
479
480 AssertPtr(pDevIns);
481 PPDMTHREAD pThread = pVM->pdm.s.pThreads;
482 while (pThread)
483 {
484 PPDMTHREAD pNext = pThread->Internal.s.pNext;
485 if ( pThread->Internal.s.enmType == PDMTHREADTYPE_DEVICE
486 && pThread->u.Dev.pDevIns == pDevIns)
487 {
488 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
489 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
490 rc = rc2;
491 }
492 pThread = pNext;
493 }
494
495 return rc;
496}
497
498
499/**
500 * Destroys all threads associated with an USB device.
501 *
502 * This function is called by PDMUsb when a device is destroyed.
503 *
504 * @returns VBox status code of the first failure.
505 * @param pVM The VM handle.
506 * @param pUsbIns The USB device instance.
507 */
508int pdmR3ThreadDestroyUsb(PVM pVM, PPDMUSBINS pUsbIns)
509{
510 int rc = VINF_SUCCESS;
511
512 AssertPtr(pUsbIns);
513 PPDMTHREAD pThread = pVM->pdm.s.pThreads;
514 while (pThread)
515 {
516 PPDMTHREAD pNext = pThread->Internal.s.pNext;
517 if ( pThread->Internal.s.enmType == PDMTHREADTYPE_DEVICE
518 && pThread->u.Usb.pUsbIns == pUsbIns)
519 {
520 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
521 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
522 rc = rc2;
523 }
524 pThread = pNext;
525 }
526
527 return rc;
528}
529
530
531/**
532 * Destroys all threads associated with a driver.
533 *
534 * This function is called by PDMDriver when a driver is destroyed.
535 *
536 * @returns VBox status code of the first failure.
537 * @param pVM The VM handle.
538 * @param pDrvIns The driver instance.
539 */
540int pdmR3ThreadDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
541{
542 int rc = VINF_SUCCESS;
543
544 AssertPtr(pDrvIns);
545 PPDMTHREAD pThread = pVM->pdm.s.pThreads;
546 while (pThread)
547 {
548 PPDMTHREAD pNext = pThread->Internal.s.pNext;
549 if ( pThread->Internal.s.enmType == PDMTHREADTYPE_DRIVER
550 && pThread->u.Drv.pDrvIns == pDrvIns)
551 {
552 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
553 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
554 rc = rc2;
555 }
556 pThread = pNext;
557 }
558
559 return rc;
560}
561
562
563/**
564 * Called For VM power off.
565 *
566 * @param pVM The VM handle.
567 */
568void pdmR3ThreadDestroyAll(PVM pVM)
569{
570 PPDMTHREAD pThread = pVM->pdm.s.pThreads;
571 while (pThread)
572 {
573 PPDMTHREAD pNext = pThread->Internal.s.pNext;
574 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
575 AssertRC(rc2);
576 pThread = pNext;
577 }
578 Assert(!pVM->pdm.s.pThreads && !pVM->pdm.s.pThreadsTail);
579}
580
581
582/**
583 * Initiate termination of the thread (self) because something failed in a bad way.
584 *
585 * @param pThread The PDM thread.
586 */
587static void pdmR3ThreadBailMeOut(PPDMTHREAD pThread)
588{
589 for (;;)
590 {
591 PDMTHREADSTATE enmState = pThread->enmState;
592 switch (enmState)
593 {
594 case PDMTHREADSTATE_SUSPENDING:
595 case PDMTHREADSTATE_SUSPENDED:
596 case PDMTHREADSTATE_RESUMING:
597 case PDMTHREADSTATE_RUNNING:
598 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
599 continue;
600 break;
601
602 case PDMTHREADSTATE_TERMINATING:
603 case PDMTHREADSTATE_TERMINATED:
604 break;
605
606 case PDMTHREADSTATE_INITIALIZING:
607 default:
608 AssertMsgFailed(("enmState=%d\n", enmState));
609 break;
610 }
611 break;
612 }
613}
614
615
616/**
617 * Called by the PDM thread in response to a wakeup call with
618 * suspending as the new state.
619 *
620 * The thread will block in side this call until the state is changed in
621 * response to a VM state change or to the device/driver/whatever calling the
622 * PDMR3ThreadResume API.
623 *
624 * @returns VBox status code.
625 * On failure, terminate the thread.
626 * @param pThread The PDM thread.
627 */
628PDMR3DECL(int) PDMR3ThreadIAmSuspending(PPDMTHREAD pThread)
629{
630 /*
631 * Assert sanity.
632 */
633 AssertPtr(pThread);
634 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
635 Assert(pThread->Thread == RTThreadSelf() || pThread->enmState == PDMTHREADSTATE_INITIALIZING);
636 PDMTHREADSTATE enmState = pThread->enmState;
637 Assert( enmState == PDMTHREADSTATE_SUSPENDING
638 || enmState == PDMTHREADSTATE_INITIALIZING);
639
640 /*
641 * Update the state, notify the control thread (the API caller) and go to sleep.
642 */
643 int rc = VERR_WRONG_ORDER;
644 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_SUSPENDED, enmState))
645 {
646 rc = RTThreadUserSignal(pThread->Thread);
647 if (RT_SUCCESS(rc))
648 {
649 rc = RTSemEventMultiWait(pThread->Internal.s.BlockEvent, RT_INDEFINITE_WAIT);
650 if ( RT_SUCCESS(rc)
651 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
652 return rc;
653
654 if (RT_SUCCESS(rc))
655 rc = VERR_INTERNAL_ERROR;
656 }
657 }
658
659 AssertMsgFailed(("rc=%d enmState=%d\n", rc, pThread->enmState));
660 pdmR3ThreadBailMeOut(pThread);
661 return rc;
662}
663
664
665/**
666 * Called by the PDM thread in response to a resuming state.
667 *
668 * The purpose of this API is to tell the PDMR3ThreadResume caller that
669 * the the PDM thread has successfully resumed. It will also do the
670 * state transition from the resuming to the running state.
671 *
672 * @returns VBox status code.
673 * On failure, terminate the thread.
674 * @param pThread The PDM thread.
675 */
676PDMR3DECL(int) PDMR3ThreadIAmRunning(PPDMTHREAD pThread)
677{
678 /*
679 * Assert sanity.
680 */
681 Assert(pThread->enmState == PDMTHREADSTATE_RESUMING);
682 Assert(pThread->Thread == RTThreadSelf());
683
684 /*
685 * Update the state and tell the control thread (the guy calling the resume API).
686 */
687 int rc = VERR_WRONG_ORDER;
688 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_RUNNING, PDMTHREADSTATE_RESUMING))
689 {
690 rc = RTThreadUserSignal(pThread->Thread);
691 if (RT_SUCCESS(rc))
692 return rc;
693 }
694
695 AssertMsgFailed(("rc=%d enmState=%d\n", rc, pThread->enmState));
696 pdmR3ThreadBailMeOut(pThread);
697 return rc;
698}
699
700
701/**
702 * Called by the PDM thread instead of RTThreadSleep.
703 *
704 * The difference is that the sleep will be interrupted on state change. The
705 * thread must be in the running state, otherwise it will return immediately.
706 *
707 * @returns VBox status code.
708 * @retval VINF_SUCCESS on success or state change.
709 * @retval VERR_INTERRUPTED on signal or APC.
710 *
711 * @param pThread The PDM thread.
712 * @param cMillies The number of milliseconds to sleep.
713 */
714PDMR3DECL(int) PDMR3ThreadSleep(PPDMTHREAD pThread, unsigned cMillies)
715{
716 /*
717 * Assert sanity.
718 */
719 AssertReturn(pThread->enmState > PDMTHREADSTATE_INVALID && pThread->enmState < PDMTHREADSTATE_TERMINATED, VERR_INTERNAL_ERROR);
720 AssertReturn(pThread->Thread == RTThreadSelf(), VERR_INTERNAL_ERROR);
721
722 /*
723 * Reset the event semaphore, check the state and sleep.
724 */
725 RTSemEventMultiReset(pThread->Internal.s.SleepEvent);
726 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
727 return VINF_SUCCESS;
728 return RTSemEventMultiWaitNoResume(pThread->Internal.s.SleepEvent, cMillies);
729}
730
731
732/**
733 * The PDM thread function.
734 *
735 * @returns return from pfnThread.
736 *
737 * @param Thread The thread handle.
738 * @param pvUser Pointer to the PDMTHREAD structure.
739 */
740static DECLCALLBACK(int) pdmR3ThreadMain(RTTHREAD Thread, void *pvUser)
741{
742 PPDMTHREAD pThread = (PPDMTHREAD)pvUser;
743 Log(("PDMThread: Initializing thread %RTthrd / %p / '%s'...\n", Thread, pThread, RTThreadGetName(Thread)));
744 pThread->Thread = Thread;
745
746 /*
747 * The run loop.
748 *
749 * It handles simple thread functions which returns when they see a suspending
750 * request and leaves the PDMR3ThreadIAmSuspending and PDMR3ThreadIAmRunning
751 * parts to us.
752 */
753 int rc;
754 for (;;)
755 {
756 switch (pThread->Internal.s.enmType)
757 {
758 case PDMTHREADTYPE_DEVICE:
759 rc = pThread->u.Dev.pfnThread(pThread->u.Dev.pDevIns, pThread);
760 break;
761
762 case PDMTHREADTYPE_USB:
763 rc = pThread->u.Usb.pfnThread(pThread->u.Usb.pUsbIns, pThread);
764 break;
765
766 case PDMTHREADTYPE_DRIVER:
767 rc = pThread->u.Drv.pfnThread(pThread->u.Drv.pDrvIns, pThread);
768 break;
769
770 case PDMTHREADTYPE_INTERNAL:
771 rc = pThread->u.Int.pfnThread(pThread->Internal.s.pVM, pThread);
772 break;
773
774 case PDMTHREADTYPE_EXTERNAL:
775 rc = pThread->u.Ext.pfnThread(pThread);
776 break;
777
778 default:
779 AssertMsgFailed(("%d\n", pThread->Internal.s.enmType));
780 rc = VERR_INTERNAL_ERROR;
781 break;
782 }
783 if (RT_FAILURE(rc))
784 break;
785
786 /*
787 * If this is a simple thread function, the state will be suspending
788 * or initializing now. If it isn't we're supposed to terminate.
789 */
790 if ( pThread->enmState != PDMTHREADSTATE_SUSPENDING
791 && pThread->enmState != PDMTHREADSTATE_INITIALIZING)
792 {
793 Assert(pThread->enmState == PDMTHREADSTATE_TERMINATING);
794 break;
795 }
796 rc = PDMR3ThreadIAmSuspending(pThread);
797 if (RT_FAILURE(rc))
798 break;
799 if (pThread->enmState != PDMTHREADSTATE_RESUMING)
800 {
801 Assert(pThread->enmState == PDMTHREADSTATE_TERMINATING);
802 break;
803 }
804
805 rc = PDMR3ThreadIAmRunning(pThread);
806 if (RT_FAILURE(rc))
807 break;
808 }
809
810 /*
811 * Advance the state to terminating and then on to terminated.
812 */
813 for (;;)
814 {
815 PDMTHREADSTATE enmState = pThread->enmState;
816 if ( enmState == PDMTHREADSTATE_TERMINATING
817 || pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
818 break;
819 }
820
821 ASMAtomicXchgSize(&pThread->enmState, PDMTHREADSTATE_TERMINATED);
822 int rc2 = RTThreadUserSignal(Thread); AssertRC(rc2);
823
824 Log(("PDMThread: Terminating thread %RTthrd / %p / '%s': %Rrc\n", Thread, pThread, RTThreadGetName(Thread), rc));
825 return rc;
826}
827
828
829/**
830 * Initiate termination of the thread because something failed in a bad way.
831 *
832 * @param pThread The PDM thread.
833 */
834static void pdmR3ThreadBailOut(PPDMTHREAD pThread)
835{
836 for (;;)
837 {
838 PDMTHREADSTATE enmState = pThread->enmState;
839 switch (enmState)
840 {
841 case PDMTHREADSTATE_SUSPENDING:
842 case PDMTHREADSTATE_SUSPENDED:
843 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
844 continue;
845 RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
846 break;
847
848 case PDMTHREADSTATE_RESUMING:
849 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
850 continue;
851 break;
852
853 case PDMTHREADSTATE_RUNNING:
854 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
855 continue;
856 pdmR3ThreadWakeUp(pThread);
857 break;
858
859 case PDMTHREADSTATE_TERMINATING:
860 case PDMTHREADSTATE_TERMINATED:
861 break;
862
863 case PDMTHREADSTATE_INITIALIZING:
864 default:
865 AssertMsgFailed(("enmState=%d\n", enmState));
866 break;
867 }
868 break;
869 }
870}
871
872
873/**
874 * Suspends the thread.
875 *
876 * This can be called at the power off / suspend notifications to suspend the
877 * PDM thread a bit early. The thread will be automatically suspend upon
878 * completion of the device/driver notification cycle.
879 *
880 * The caller is responsible for serializing the control operations on the
881 * thread. That basically means, always do these calls from the EMT.
882 *
883 * @returns VBox status code.
884 * @param pThread The PDM thread.
885 */
886PDMR3DECL(int) PDMR3ThreadSuspend(PPDMTHREAD pThread)
887{
888 /*
889 * Assert sanity.
890 */
891 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
892 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
893 Assert(pThread->Thread != RTThreadSelf());
894
895 /*
896 * Change the state to resuming and kick the thread.
897 */
898 int rc = RTSemEventMultiReset(pThread->Internal.s.BlockEvent);
899 if (RT_SUCCESS(rc))
900 {
901 rc = RTThreadUserReset(pThread->Thread);
902 if (RT_SUCCESS(rc))
903 {
904 rc = VERR_WRONG_ORDER;
905 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_SUSPENDING, PDMTHREADSTATE_RUNNING))
906 {
907 rc = pdmR3ThreadWakeUp(pThread);
908 if (RT_SUCCESS(rc))
909 {
910 /*
911 * Wait for the thread to reach the suspended state.
912 */
913 if (pThread->enmState != PDMTHREADSTATE_SUSPENDED)
914 rc = RTThreadUserWait(pThread->Thread, 60*1000);
915 if ( RT_SUCCESS(rc)
916 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
917 rc = VERR_INTERNAL_ERROR;
918 if (RT_SUCCESS(rc))
919 return rc;
920 }
921 }
922 }
923 }
924
925 /*
926 * Something failed, initialize termination.
927 */
928 AssertMsgFailed(("PDMR3ThreadSuspend -> rc=%Rrc enmState=%d\n", rc, pThread->enmState));
929 pdmR3ThreadBailOut(pThread);
930 return rc;
931}
932
933
934/**
935 * Suspend all running threads.
936 *
937 * This is called by PDMR3Suspend() and PDMR3PowerOff() after all the devices
938 * and drivers have been notified about the suspend / power off.
939 *
940 * @return VBox status code.
941 * @param pVM The VM handle.
942 */
943int pdmR3ThreadSuspendAll(PVM pVM)
944{
945 for (PPDMTHREAD pThread = pVM->pdm.s.pThreads; pThread; pThread = pThread->Internal.s.pNext)
946 switch (pThread->enmState)
947 {
948 case PDMTHREADSTATE_RUNNING:
949 {
950 int rc = PDMR3ThreadSuspend(pThread);
951 AssertRCReturn(rc, rc);
952 break;
953 }
954
955 /* suspend -> power off; voluntary suspend. */
956 case PDMTHREADSTATE_SUSPENDED:
957 break;
958
959 default:
960 AssertMsgFailed(("pThread=%p enmState=%d\n", pThread, pThread->enmState));
961 break;
962 }
963 return VINF_SUCCESS;
964}
965
966
967/**
968 * Resumes the thread.
969 *
970 * This can be called the power on / resume notifications to resume the
971 * PDM thread a bit early. The thread will be automatically resumed upon
972 * return from these two notification callbacks (devices/drivers).
973 *
974 * The caller is responsible for serializing the control operations on the
975 * thread. That basically means, always do these calls from the EMT.
976 *
977 * @returns VBox status code.
978 * @param pThread The PDM thread.
979 */
980PDMR3DECL(int) PDMR3ThreadResume(PPDMTHREAD pThread)
981{
982 /*
983 * Assert sanity.
984 */
985 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
986 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
987 Assert(pThread->Thread != RTThreadSelf());
988
989 /*
990 * Change the state to resuming and kick the thread.
991 */
992 int rc = RTThreadUserReset(pThread->Thread);
993 if (RT_SUCCESS(rc))
994 {
995 rc = VERR_WRONG_ORDER;
996 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_RESUMING, PDMTHREADSTATE_SUSPENDED))
997 {
998 rc = RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
999 if (RT_SUCCESS(rc))
1000 {
1001 /*
1002 * Wait for the thread to reach the running state.
1003 */
1004 rc = RTThreadUserWait(pThread->Thread, 60*1000);
1005 if ( RT_SUCCESS(rc)
1006 && pThread->enmState != PDMTHREADSTATE_RUNNING)
1007 rc = VERR_INTERNAL_ERROR;
1008 if (RT_SUCCESS(rc))
1009 return rc;
1010 }
1011 }
1012 }
1013
1014 /*
1015 * Something failed, initialize termination.
1016 */
1017 AssertMsgFailed(("PDMR3ThreadResume -> rc=%Rrc enmState=%d\n", rc, pThread->enmState));
1018 pdmR3ThreadBailOut(pThread);
1019 return rc;
1020}
1021
1022
1023/**
1024 * Resumes all threads not running.
1025 *
1026 * This is called by PDMR3Resume() and PDMR3PowerOn() after all the devices
1027 * and drivers have been notified about the resume / power on .
1028 *
1029 * @return VBox status code.
1030 * @param pVM The VM handle.
1031 */
1032int pdmR3ThreadResumeAll(PVM pVM)
1033{
1034 for (PPDMTHREAD pThread = pVM->pdm.s.pThreads; pThread; pThread = pThread->Internal.s.pNext)
1035 switch (pThread->enmState)
1036 {
1037 case PDMTHREADSTATE_SUSPENDED:
1038 {
1039 int rc = PDMR3ThreadResume(pThread);
1040 AssertRCReturn(rc, rc);
1041 break;
1042 }
1043
1044 default:
1045 AssertMsgFailed(("pThread=%p enmState=%d\n", pThread, pThread->enmState));
1046 break;
1047 }
1048 return VINF_SUCCESS;
1049}
1050
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