VirtualBox

source: vbox/trunk/src/VBox/Devices/testcase/tstDevicePdmThread.cpp

Last change on this file was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use