VirtualBox

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

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

pdmR3ThreadMain: Added LogRel on unexpected thread termination.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 32.9 KB
Line 
1/* $Id: PDMThread.cpp 15540 2008-12-15 20:01:28Z 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 */
307VMMR3DECL(int) PDMR3ThreadCreate(PVM pVM, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADINT pfnThread,
308 PFNPDMTHREADWAKEUPINT pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
309{
310 int rc = pdmR3ThreadNew(pVM, ppThread);
311 if (RT_SUCCESS(rc))
312 {
313 PPDMTHREAD pThread = *ppThread;
314 pThread->pvUser = pvUser;
315 pThread->Internal.s.enmType = PDMTHREADTYPE_INTERNAL;
316 pThread->u.Int.pfnThread = pfnThread;
317 pThread->u.Int.pfnWakeUp = pfnWakeUp;
318 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
319 }
320 return rc;
321}
322
323
324/**
325 * Creates a PDM thread for VM use by some external party.
326 *
327 * @returns VBox status code.
328 * @param pVM The VM handle.
329 * @param ppThread Where to store the thread 'handle'.
330 * @param pvUser The user argument to the thread function.
331 * @param pfnThread The thread function.
332 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
333 * a state change is pending.
334 * @param cbStack See RTThreadCreate.
335 * @param enmType See RTThreadCreate.
336 * @param pszName See RTThreadCreate.
337 */
338VMMR3DECL(int) PDMR3ThreadCreateExternal(PVM pVM, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADEXT pfnThread,
339 PFNPDMTHREADWAKEUPEXT pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
340{
341 int rc = pdmR3ThreadNew(pVM, ppThread);
342 if (RT_SUCCESS(rc))
343 {
344 PPDMTHREAD pThread = *ppThread;
345 pThread->pvUser = pvUser;
346 pThread->Internal.s.enmType = PDMTHREADTYPE_EXTERNAL;
347 pThread->u.Ext.pfnThread = pfnThread;
348 pThread->u.Ext.pfnWakeUp = pfnWakeUp;
349 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
350 }
351 return rc;
352}
353
354
355/**
356 * Destroys a PDM thread.
357 *
358 * This will wakeup the thread, tell it to terminate, and wait for it terminate.
359 *
360 * @returns VBox status code.
361 * This reflects the success off destroying the thread and not the exit code
362 * of the thread as this is stored in *pRcThread.
363 * @param pThread The thread to destroy.
364 * @param pRcThread Where to store the thread exit code. Optional.
365 * @thread The emulation thread (EMT).
366 */
367VMMR3DECL(int) PDMR3ThreadDestroy(PPDMTHREAD pThread, int *pRcThread)
368{
369 /*
370 * Assert sanity.
371 */
372 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
373 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
374 Assert(pThread->Thread != RTThreadSelf());
375 AssertPtrNullReturn(pRcThread, VERR_INVALID_POINTER);
376 PVM pVM = pThread->Internal.s.pVM;
377 VM_ASSERT_EMT(pVM);
378
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 */
628VMMR3DECL(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 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 */
676VMMR3DECL(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 */
714VMMR3DECL(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 if (RT_FAILURE(rc))
811 LogRel(("PDMThread: Thread '%s' (%RTthrd) quit unexpectedly with rc=%Rrc.\n", RTThreadGetName(Thread), Thread, rc));
812
813 /*
814 * Advance the state to terminating and then on to terminated.
815 */
816 for (;;)
817 {
818 PDMTHREADSTATE enmState = pThread->enmState;
819 if ( enmState == PDMTHREADSTATE_TERMINATING
820 || pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
821 break;
822 }
823
824 ASMAtomicXchgSize(&pThread->enmState, PDMTHREADSTATE_TERMINATED);
825 int rc2 = RTThreadUserSignal(Thread); AssertRC(rc2);
826
827 Log(("PDMThread: Terminating thread %RTthrd / %p / '%s': %Rrc\n", Thread, pThread, RTThreadGetName(Thread), rc));
828 return rc;
829}
830
831
832/**
833 * Initiate termination of the thread because something failed in a bad way.
834 *
835 * @param pThread The PDM thread.
836 */
837static void pdmR3ThreadBailOut(PPDMTHREAD pThread)
838{
839 for (;;)
840 {
841 PDMTHREADSTATE enmState = pThread->enmState;
842 switch (enmState)
843 {
844 case PDMTHREADSTATE_SUSPENDING:
845 case PDMTHREADSTATE_SUSPENDED:
846 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
847 continue;
848 RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
849 break;
850
851 case PDMTHREADSTATE_RESUMING:
852 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
853 continue;
854 break;
855
856 case PDMTHREADSTATE_RUNNING:
857 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
858 continue;
859 pdmR3ThreadWakeUp(pThread);
860 break;
861
862 case PDMTHREADSTATE_TERMINATING:
863 case PDMTHREADSTATE_TERMINATED:
864 break;
865
866 case PDMTHREADSTATE_INITIALIZING:
867 default:
868 AssertMsgFailed(("enmState=%d\n", enmState));
869 break;
870 }
871 break;
872 }
873}
874
875
876/**
877 * Suspends the thread.
878 *
879 * This can be called at the power off / suspend notifications to suspend the
880 * PDM thread a bit early. The thread will be automatically suspend upon
881 * completion of the device/driver notification cycle.
882 *
883 * The caller is responsible for serializing the control operations on the
884 * thread. That basically means, always do these calls from the EMT.
885 *
886 * @returns VBox status code.
887 * @param pThread The PDM thread.
888 */
889VMMR3DECL(int) PDMR3ThreadSuspend(PPDMTHREAD pThread)
890{
891 /*
892 * Assert sanity.
893 */
894 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
895 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
896 Assert(pThread->Thread != RTThreadSelf());
897
898 /*
899 * Change the state to resuming and kick the thread.
900 */
901 int rc = RTSemEventMultiReset(pThread->Internal.s.BlockEvent);
902 if (RT_SUCCESS(rc))
903 {
904 rc = RTThreadUserReset(pThread->Thread);
905 if (RT_SUCCESS(rc))
906 {
907 rc = VERR_WRONG_ORDER;
908 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_SUSPENDING, PDMTHREADSTATE_RUNNING))
909 {
910 rc = pdmR3ThreadWakeUp(pThread);
911 if (RT_SUCCESS(rc))
912 {
913 /*
914 * Wait for the thread to reach the suspended state.
915 */
916 if (pThread->enmState != PDMTHREADSTATE_SUSPENDED)
917 rc = RTThreadUserWait(pThread->Thread, 60*1000);
918 if ( RT_SUCCESS(rc)
919 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
920 rc = VERR_INTERNAL_ERROR;
921 if (RT_SUCCESS(rc))
922 return rc;
923 }
924 }
925 }
926 }
927
928 /*
929 * Something failed, initialize termination.
930 */
931 AssertMsgFailed(("PDMR3ThreadSuspend -> rc=%Rrc enmState=%d\n", rc, pThread->enmState));
932 pdmR3ThreadBailOut(pThread);
933 return rc;
934}
935
936
937/**
938 * Suspend all running threads.
939 *
940 * This is called by PDMR3Suspend() and PDMR3PowerOff() after all the devices
941 * and drivers have been notified about the suspend / power off.
942 *
943 * @return VBox status code.
944 * @param pVM The VM handle.
945 */
946int pdmR3ThreadSuspendAll(PVM pVM)
947{
948 for (PPDMTHREAD pThread = pVM->pdm.s.pThreads; pThread; pThread = pThread->Internal.s.pNext)
949 switch (pThread->enmState)
950 {
951 case PDMTHREADSTATE_RUNNING:
952 {
953 int rc = PDMR3ThreadSuspend(pThread);
954 AssertRCReturn(rc, rc);
955 break;
956 }
957
958 /* suspend -> power off; voluntary suspend. */
959 case PDMTHREADSTATE_SUSPENDED:
960 break;
961
962 default:
963 AssertMsgFailed(("pThread=%p enmState=%d\n", pThread, pThread->enmState));
964 break;
965 }
966 return VINF_SUCCESS;
967}
968
969
970/**
971 * Resumes the thread.
972 *
973 * This can be called the power on / resume notifications to resume the
974 * PDM thread a bit early. The thread will be automatically resumed upon
975 * return from these two notification callbacks (devices/drivers).
976 *
977 * The caller is responsible for serializing the control operations on the
978 * thread. That basically means, always do these calls from the EMT.
979 *
980 * @returns VBox status code.
981 * @param pThread The PDM thread.
982 */
983VMMR3DECL(int) PDMR3ThreadResume(PPDMTHREAD pThread)
984{
985 /*
986 * Assert sanity.
987 */
988 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
989 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
990 Assert(pThread->Thread != RTThreadSelf());
991
992 /*
993 * Change the state to resuming and kick the thread.
994 */
995 int rc = RTThreadUserReset(pThread->Thread);
996 if (RT_SUCCESS(rc))
997 {
998 rc = VERR_WRONG_ORDER;
999 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_RESUMING, PDMTHREADSTATE_SUSPENDED))
1000 {
1001 rc = RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
1002 if (RT_SUCCESS(rc))
1003 {
1004 /*
1005 * Wait for the thread to reach the running state.
1006 */
1007 rc = RTThreadUserWait(pThread->Thread, 60*1000);
1008 if ( RT_SUCCESS(rc)
1009 && pThread->enmState != PDMTHREADSTATE_RUNNING)
1010 rc = VERR_INTERNAL_ERROR;
1011 if (RT_SUCCESS(rc))
1012 return rc;
1013 }
1014 }
1015 }
1016
1017 /*
1018 * Something failed, initialize termination.
1019 */
1020 AssertMsgFailed(("PDMR3ThreadResume -> rc=%Rrc enmState=%d\n", rc, pThread->enmState));
1021 pdmR3ThreadBailOut(pThread);
1022 return rc;
1023}
1024
1025
1026/**
1027 * Resumes all threads not running.
1028 *
1029 * This is called by PDMR3Resume() and PDMR3PowerOn() after all the devices
1030 * and drivers have been notified about the resume / power on .
1031 *
1032 * @return VBox status code.
1033 * @param pVM The VM handle.
1034 */
1035int pdmR3ThreadResumeAll(PVM pVM)
1036{
1037 for (PPDMTHREAD pThread = pVM->pdm.s.pThreads; pThread; pThread = pThread->Internal.s.pNext)
1038 switch (pThread->enmState)
1039 {
1040 case PDMTHREADSTATE_SUSPENDED:
1041 {
1042 int rc = PDMR3ThreadResume(pThread);
1043 AssertRCReturn(rc, rc);
1044 break;
1045 }
1046
1047 default:
1048 AssertMsgFailed(("pThread=%p enmState=%d\n", pThread, pThread->enmState));
1049 break;
1050 }
1051 return VINF_SUCCESS;
1052}
1053
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use