VirtualBox

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

Last change on this file since 3523 was 3523, checked in by vboxsync, 18 years ago

PDMThread (unfinished).

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette