VirtualBox

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

Last change on this file since 107044 was 106171, checked in by vboxsync, 2 months ago

VMM/PDMThread: More thread failure details in the release log.

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