VirtualBox

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

Last change on this file since 106129 was 106061, checked in by vboxsync, 2 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
Line 
1/* $Id: tstDevicePdmThread.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * PDM Thread - Thread Management (copied from PDMThread).
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 <VBox/err.h>
34#include <VBox/log.h>
35
36#include <iprt/asm.h>
37#include <iprt/semaphore.h>
38#include <iprt/assert.h>
39#include <iprt/mem.h>
40#include <iprt/thread.h>
41
42#include "tstDeviceInternal.h"
43
44
45/*********************************************************************************************************************************
46* Internal Functions *
47*********************************************************************************************************************************/
48static DECLCALLBACK(int) pdmR3ThreadMain(RTTHREAD Thread, void *pvUser);
49
50
51/**
52 * Wrapper around ASMAtomicCmpXchgSize.
53 */
54DECLINLINE(bool) pdmR3AtomicCmpXchgState(PPDMTHREAD pThread, PDMTHREADSTATE enmNewState, PDMTHREADSTATE enmOldState)
55{
56 bool fRc;
57 ASMAtomicCmpXchgSize(&pThread->enmState, enmNewState, enmOldState, fRc);
58 return fRc;
59}
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 */
68static DECLCALLBACK(int) pdmR3ThreadWakeUp(PPDMTHREAD pThread)
69{
70 RTSemEventMultiSignal(pThread->Internal.s.SleepEvent);
71
72 int rc;
73 switch (pThread->Internal.s.enmType)
74 {
75 case PDMTHREADTYPE_DEVICE:
76 rc = pThread->u.Dev.pfnWakeUp(pThread->u.Dev.pDevIns, pThread);
77 break;
78
79 case PDMTHREADTYPE_USB:
80 rc = pThread->u.Usb.pfnWakeUp(pThread->u.Usb.pUsbIns, pThread);
81 break;
82
83 case PDMTHREADTYPE_DRIVER:
84 rc = pThread->u.Drv.pfnWakeUp(pThread->u.Drv.pDrvIns, pThread);
85 break;
86
87 case PDMTHREADTYPE_INTERNAL:
88 rc = pThread->u.Int.pfnWakeUp(NULL /*pVM*/, pThread);
89 break;
90
91 case PDMTHREADTYPE_EXTERNAL:
92 rc = pThread->u.Ext.pfnWakeUp(pThread);
93 break;
94
95 default:
96 AssertMsgFailed(("%d\n", pThread->Internal.s.enmType));
97 rc = VERR_PDM_THREAD_IPE_1;
98 break;
99 }
100 AssertRC(rc);
101 return rc;
102}
103
104
105/**
106 * Allocates new thread instance.
107 *
108 * @returns VBox status code.
109 * @param pDut The device under test.
110 * @param ppThread Where to store the pointer to the instance.
111 */
112static int pdmR3ThreadNew(PTSTDEVDUTINT pDut, PPPDMTHREAD ppThread)
113{
114 PPDMTHREAD pThread = (PPDMTHREAD)RTMemAllocZ(sizeof(*pThread));
115 if (!pThread)
116 return VERR_NO_MEMORY;
117
118 pThread->u32Version = PDMTHREAD_VERSION;
119 pThread->enmState = PDMTHREADSTATE_INITIALIZING;
120 pThread->Thread = NIL_RTTHREAD;
121 pThread->Internal.s.pDut = pDut;
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.
133 * @param pDut The device under test.
134 * @param ppThread Where the thread instance data handle is.
135 * @param cbStack The stack size, see RTThreadCreate().
136 * @param enmType The thread type, see RTThreadCreate().
137 * @param pszName The thread name, see RTThreadCreate().
138 */
139static int pdmR3ThreadInit(PTSTDEVDUTINT pDut, PPPDMTHREAD ppThread, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
140{
141 PPDMTHREAD pThread = *ppThread;
142
143 int rc = RTSemEventMultiCreate(&pThread->Internal.s.BlockEvent);
144 if (RT_SUCCESS(rc))
145 {
146 rc = RTSemEventMultiCreate(&pThread->Internal.s.SleepEvent);
147 if (RT_SUCCESS(rc))
148 {
149 /*
150 * Create the thread and wait for it to initialize.
151 * The newly created thread will set the PDMTHREAD::Thread member.
152 */
153 RTTHREAD Thread;
154 rc = RTThreadCreate(&Thread, pdmR3ThreadMain, pThread, cbStack, enmType, RTTHREADFLAGS_WAITABLE, pszName);
155 if (RT_SUCCESS(rc))
156 {
157 rc = RTThreadUserWait(Thread, 60*1000);
158 if ( RT_SUCCESS(rc)
159 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
160 rc = VERR_PDM_THREAD_IPE_2;
161 if (RT_SUCCESS(rc))
162 {
163 /*
164 * Insert it into the thread list.
165 */
166 tstDevDutLockExcl(pDut);
167 RTListAppend(&pDut->LstPdmThreads, &pThread->Internal.s.NdPdmThrds);
168 tstDevDutUnlockExcl(pDut);
169
170 rc = RTThreadUserReset(Thread);
171 AssertRC(rc);
172 return rc;
173 }
174
175 /* bailout */
176 RTThreadWait(Thread, 60*1000, NULL);
177 }
178 RTSemEventMultiDestroy(pThread->Internal.s.SleepEvent);
179 pThread->Internal.s.SleepEvent = NIL_RTSEMEVENTMULTI;
180 }
181 RTSemEventMultiDestroy(pThread->Internal.s.BlockEvent);
182 pThread->Internal.s.BlockEvent = NIL_RTSEMEVENTMULTI;
183 }
184 RTMemFree(pThread);
185 *ppThread = NULL;
186
187 return rc;
188}
189
190
191/**
192 * Device Helper for creating a thread associated with a device.
193 *
194 * @returns VBox status code.
195 * @param pDut The device under test.
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.
200 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
201 * a state change is pending.
202 * @param cbStack See RTThreadCreate.
203 * @param enmType See RTThreadCreate.
204 * @param pszName See RTThreadCreate.
205 */
206DECLHIDDEN(int) tstDevPdmR3ThreadCreateDevice(PTSTDEVDUTINT pDut, PPDMDEVINS pDevIns, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADDEV pfnThread,
207 PFNPDMTHREADWAKEUPDEV pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
208{
209 int rc = pdmR3ThreadNew(pDut, ppThread);
210 if (RT_SUCCESS(rc))
211 {
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;
218 rc = pdmR3ThreadInit(pDut, ppThread, cbStack, enmType, pszName);
219 }
220 return rc;
221}
222
223
224/**
225 * USB Device Helper for creating a thread associated with an USB device.
226 *
227 * @returns VBox status code.
228 * @param pDut The device under test.
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.
233 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
234 * a state change is pending.
235 * @param cbStack See RTThreadCreate.
236 * @param enmType See RTThreadCreate.
237 * @param pszName See RTThreadCreate.
238 */
239DECLHIDDEN(int) tstDevPdmR3ThreadCreateUsb(PTSTDEVDUTINT pDut, PPDMUSBINS pUsbIns, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADUSB pfnThread,
240 PFNPDMTHREADWAKEUPUSB pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
241{
242 int rc = pdmR3ThreadNew(pDut, ppThread);
243 if (RT_SUCCESS(rc))
244 {
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;
251 rc = pdmR3ThreadInit(pDut, ppThread, cbStack, enmType, pszName);
252 }
253 return rc;
254}
255
256
257/**
258 * Driver Helper for creating a thread associated with a driver.
259 *
260 * @returns VBox status code.
261 * @param pDut The device under test.
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.
266 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
267 * a state change is pending.
268 * @param cbStack See RTThreadCreate.
269 * @param enmType See RTThreadCreate.
270 * @param pszName See RTThreadCreate.
271 */
272DECLHIDDEN(int) tstDevPdmR3ThreadCreateDriver(PTSTDEVDUTINT pDut, PPDMDRVINS pDrvIns, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADDRV pfnThread,
273 PFNPDMTHREADWAKEUPDRV pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
274{
275 int rc = pdmR3ThreadNew(pDut, ppThread);
276 if (RT_SUCCESS(rc))
277 {
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;
284 rc = pdmR3ThreadInit(pDut, ppThread, cbStack, enmType, pszName);
285 }
286 return rc;
287}
288
289
290/**
291 * Creates a PDM thread for internal use in the VM.
292 *
293 * @returns VBox status code.
294 * @param pDut The device under test.
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.
298 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
299 * a state change is pending.
300 * @param cbStack See RTThreadCreate.
301 * @param enmType See RTThreadCreate.
302 * @param pszName See RTThreadCreate.
303 */
304DECLHIDDEN(int) tstDevPdmR3ThreadCreate(PTSTDEVDUTINT pDut, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADINT pfnThread,
305 PFNPDMTHREADWAKEUPINT pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
306{
307 int rc = pdmR3ThreadNew(pDut, ppThread);
308 if (RT_SUCCESS(rc))
309 {
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;
315 rc = pdmR3ThreadInit(pDut, ppThread, cbStack, enmType, pszName);
316 }
317 return rc;
318}
319
320
321/**
322 * Creates a PDM thread for VM use by some external party.
323 *
324 * @returns VBox status code.
325 * @param pDut The device under test.
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.
329 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
330 * a state change is pending.
331 * @param cbStack See RTThreadCreate.
332 * @param enmType See RTThreadCreate.
333 * @param pszName See RTThreadCreate.
334 */
335DECLHIDDEN(int) tstDevPdmR3ThreadCreateExternal(PTSTDEVDUTINT pDut, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADEXT pfnThread,
336 PFNPDMTHREADWAKEUPEXT pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
337{
338 int rc = pdmR3ThreadNew(pDut, ppThread);
339 if (RT_SUCCESS(rc))
340 {
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;
346 rc = pdmR3ThreadInit(pDut, ppThread, cbStack, enmType, pszName);
347 }
348 return rc;
349}
350
351
352/**
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.
358 * This reflects the success off destroying the thread and not the exit code
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 */
364DECLHIDDEN(int) tstDevPdmR3ThreadDestroy(PPDMTHREAD pThread, int *pRcThread)
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);
373
374 PTSTDEVDUTINT pDut = pThread->Internal.s.pDut;
375
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:
388 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
389 continue;
390 rc = pdmR3ThreadWakeUp(pThread);
391 break;
392
393 case PDMTHREADSTATE_SUSPENDED:
394 case PDMTHREADSTATE_SUSPENDING:
395 case PDMTHREADSTATE_RESUMING:
396 case PDMTHREADSTATE_INITIALIZING:
397 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
398 continue;
399 break;
400
401 case PDMTHREADSTATE_TERMINATING:
402 case PDMTHREADSTATE_TERMINATED:
403 break;
404
405 default:
406 AssertMsgFailed(("enmState=%d\n", enmState));
407 rc = VERR_PDM_THREAD_IPE_2;
408 break;
409 }
410 break;
411 }
412 }
413 int rc2 = RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
414 AssertRC(rc2);
415
416 /*
417 * Wait for it to terminate and the do cleanups.
418 */
419 rc2 = RTThreadWait(pThread->Thread, RT_SUCCESS(rc) ? 60*1000 : 150, pRcThread);
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 */
428 tstDevDutLockExcl(pDut);
429 RTListNodeRemove(&pThread->Internal.s.NdPdmThrds);
430 tstDevDutUnlockExcl(pDut);
431
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
439 RTMemFree(pThread);
440 }
441 else if (RT_SUCCESS(rc))
442 rc = rc2;
443
444 return rc;
445}
446
447
448/**
449 * Destroys all threads associated with a device.
450 *
451 * This function is called by PDMDevice when a device is
452 * destroyed (not currently implemented).
453 *
454 * @returns VBox status code of the first failure.
455 * @param pDut The device under test.
456 * @param pDevIns the device instance.
457 */
458DECLHIDDEN(int) tstDevPdmR3ThreadDestroyDevice(PTSTDEVDUTINT pDut, PPDMDEVINS pDevIns)
459{
460 int rc = VINF_SUCCESS;
461
462 AssertPtr(pDevIns);
463
464 tstDevDutLockExcl(pDut);
465#if 0
466 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
467 while (pThread)
468 {
469 PPDMTHREAD pNext = pThread->Internal.s.pNext;
470 if ( pThread->Internal.s.enmType == PDMTHREADTYPE_DEVICE
471 && pThread->u.Dev.pDevIns == pDevIns)
472 {
473 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
474 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
475 rc = rc2;
476 }
477 pThread = pNext;
478 }
479#endif
480 tstDevDutUnlockExcl(pDut);
481 return rc;
482}
483
484
485/**
486 * Destroys all threads associated with an USB device.
487 *
488 * This function is called by PDMUsb when a device is destroyed.
489 *
490 * @returns VBox status code of the first failure.
491 * @param pDut The device under test.
492 * @param pUsbIns The USB device instance.
493 */
494DECLHIDDEN(int) tstDevPdmR3ThreadDestroyUsb(PTSTDEVDUTINT pDut, PPDMUSBINS pUsbIns)
495{
496 int rc = VINF_SUCCESS;
497
498 AssertPtr(pUsbIns);
499
500 tstDevDutLockExcl(pDut);
501#if 0
502 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
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 }
515#endif
516 tstDevDutUnlockExcl(pDut);
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.
525 *
526 * @returns VBox status code of the first failure.
527 * @param pDut The device under test.
528 * @param pDrvIns The driver instance.
529 */
530DECLHIDDEN(int) tstDevPdmR3ThreadDestroyDriver(PTSTDEVDUTINT pDut, PPDMDRVINS pDrvIns)
531{
532 int rc = VINF_SUCCESS;
533
534 AssertPtr(pDrvIns);
535
536 tstDevDutLockExcl(pDut);
537#if 0
538 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
539 while (pThread)
540 {
541 PPDMTHREAD pNext = pThread->Internal.s.pNext;
542 if ( pThread->Internal.s.enmType == PDMTHREADTYPE_DRIVER
543 && pThread->u.Drv.pDrvIns == pDrvIns)
544 {
545 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
546 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
547 rc = rc2;
548 }
549 pThread = pNext;
550 }
551#endif
552 tstDevDutUnlockExcl(pDut);
553 return rc;
554}
555
556
557/**
558 * Called For VM power off.
559 *
560 * @param pDut The device under test.
561 */
562DECLHIDDEN(void) tstDevPdmR3ThreadDestroyAll(PTSTDEVDUTINT pDut)
563{
564 tstDevDutLockExcl(pDut);
565#if 0
566 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
567 while (pThread)
568 {
569 PPDMTHREAD pNext = pThread->Internal.s.pNext;
570 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
571 AssertRC(rc2);
572 pThread = pNext;
573 }
574 Assert(!pUVM->pdm.s.pThreads && !pUVM->pdm.s.pThreadsTail);
575#endif
576 tstDevDutUnlockExcl(pDut);
577}
578
579
580/**
581 * Initiate termination of the thread (self) because something failed in a bad way.
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:
596 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
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/**
615 * Called by the PDM thread in response to a wakeup call with
616 * suspending as the new state.
617 *
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
620 * PDMR3ThreadResume API.
621 *
622 * @returns VBox status code.
623 * On failure, terminate the thread.
624 * @param pThread The PDM thread.
625 */
626DECLHIDDEN(int) tstDevPdmR3ThreadIAmSuspending(PPDMTHREAD pThread)
627{
628 /*
629 * Assert sanity.
630 */
631 AssertPtr(pThread);
632 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
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;
642 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_SUSPENDED, enmState))
643 {
644 rc = RTThreadUserSignal(pThread->Thread);
645 if (RT_SUCCESS(rc))
646 {
647 rc = RTSemEventMultiWait(pThread->Internal.s.BlockEvent, RT_INDEFINITE_WAIT);
648 if ( RT_SUCCESS(rc)
649 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
650 return rc;
651
652 if (RT_SUCCESS(rc))
653 rc = VERR_PDM_THREAD_IPE_2;
654 }
655 }
656
657 AssertMsgFailed(("rc=%d enmState=%d\n", rc, pThread->enmState));
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
667 * the PDM thread has successfully resumed. It will also do the
668 * state transition from the resuming to the running state.
669 *
670 * @returns VBox status code.
671 * On failure, terminate the thread.
672 * @param pThread The PDM thread.
673 */
674DECLHIDDEN(int) tstDevPdmR3ThreadIAmRunning(PPDMTHREAD pThread)
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;
686 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_RUNNING, PDMTHREADSTATE_RESUMING))
687 {
688 rc = RTThreadUserSignal(pThread->Thread);
689 if (RT_SUCCESS(rc))
690 return rc;
691 }
692
693 AssertMsgFailed(("rc=%d enmState=%d\n", rc, pThread->enmState));
694 pdmR3ThreadBailMeOut(pThread);
695 return rc;
696}
697
698
699/**
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 */
712DECLHIDDEN(int) tstDevPdmR3ThreadSleep(PPDMTHREAD pThread, RTMSINTERVAL cMillies)
713{
714 /*
715 * Assert sanity.
716 */
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);
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/**
731 * The PDM thread function.
732 *
733 * @returns return from pfnThread.
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)));
742 pThread->Thread = Thread;
743
744 /*
745 * The run loop.
746 *
747 * It handles simple thread functions which returns when they see a suspending
748 * request and leaves the PDMR3ThreadIAmSuspending and PDMR3ThreadIAmRunning
749 * parts to us.
750 */
751 int rc;
752 for (;;)
753 {
754 switch (pThread->Internal.s.enmType)
755 {
756 case PDMTHREADTYPE_DEVICE:
757 rc = pThread->u.Dev.pfnThread(pThread->u.Dev.pDevIns, pThread);
758 break;
759
760 case PDMTHREADTYPE_USB:
761 rc = pThread->u.Usb.pfnThread(pThread->u.Usb.pUsbIns, pThread);
762 break;
763
764 case PDMTHREADTYPE_DRIVER:
765 rc = pThread->u.Drv.pfnThread(pThread->u.Drv.pDrvIns, pThread);
766 break;
767
768 case PDMTHREADTYPE_INTERNAL:
769 rc = pThread->u.Int.pfnThread(NULL /*pVM*/, pThread);
770 break;
771
772 case PDMTHREADTYPE_EXTERNAL:
773 rc = pThread->u.Ext.pfnThread(pThread);
774 break;
775
776 default:
777 AssertMsgFailed(("%d\n", pThread->Internal.s.enmType));
778 rc = VERR_PDM_THREAD_IPE_1;
779 break;
780 }
781 if (RT_FAILURE(rc))
782 break;
783
784 /*
785 * If this is a simple thread function, the state will be suspending
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 }
794 rc = tstDevPdmR3ThreadIAmSuspending(pThread);
795 if (RT_FAILURE(rc))
796 break;
797 if (pThread->enmState != PDMTHREADSTATE_RESUMING)
798 {
799 Assert(pThread->enmState == PDMTHREADSTATE_TERMINATING);
800 break;
801 }
802
803 rc = tstDevPdmR3ThreadIAmRunning(pThread);
804 if (RT_FAILURE(rc))
805 break;
806 }
807
808 if (RT_FAILURE(rc))
809 LogRel(("PDMThread: Thread '%s' (%RTthrd) quit unexpectedly with rc=%Rrc.\n", RTThreadGetName(Thread), Thread, rc));
810
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
818 || pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
819 break;
820 }
821
822 ASMAtomicXchgSize(&pThread->enmState, PDMTHREADSTATE_TERMINATED);
823 int rc2 = RTThreadUserSignal(Thread); AssertRC(rc2);
824
825 Log(("PDMThread: Terminating thread %RTthrd / %p / '%s': %Rrc\n", Thread, pThread, RTThreadGetName(Thread), rc));
826 return rc;
827}
828
829
830/**
831 * Initiate termination of the thread because something failed in a bad way.
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:
844 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
845 continue;
846 RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
847 break;
848
849 case PDMTHREADSTATE_RESUMING:
850 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
851 continue;
852 break;
853
854 case PDMTHREADSTATE_RUNNING:
855 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
856 continue;
857 pdmR3ThreadWakeUp(pThread);
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 *
877 * This can be called at the power off / suspend notifications to suspend the
878 * PDM thread a bit early. The thread will be automatically suspend upon
879 * completion of the device/driver notification cycle.
880 *
881 * The caller is responsible for serializing the control operations on the
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 */
887DECLHIDDEN(int) tstDevPdmR3ThreadSuspend(PPDMTHREAD pThread)
888{
889 /*
890 * Assert sanity.
891 */
892 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
893 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
894 Assert(pThread->Thread != RTThreadSelf());
895
896 /*
897 * This is a noop if the thread is already suspended.
898 */
899 if (pThread->enmState == PDMTHREADSTATE_SUSPENDED)
900 return VINF_SUCCESS;
901
902 /*
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;
912 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_SUSPENDING, PDMTHREADSTATE_RUNNING))
913 {
914 rc = pdmR3ThreadWakeUp(pThread);
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);
922 if ( RT_SUCCESS(rc)
923 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
924 rc = VERR_PDM_THREAD_IPE_2;
925 if (RT_SUCCESS(rc))
926 return rc;
927 }
928 }
929 }
930 }
931
932 /*
933 * Something failed, initialize termination.
934 */
935 AssertMsgFailed(("PDMR3ThreadSuspend -> rc=%Rrc enmState=%d suspending '%s'\n",
936 rc, pThread->enmState, RTThreadGetName(pThread->Thread)));
937 pdmR3ThreadBailOut(pThread);
938 return rc;
939}
940
941
942/**
943 * Resumes the thread.
944 *
945 * This can be called the power on / resume notifications to resume the
946 * PDM thread a bit early. The thread will be automatically resumed upon
947 * return from these two notification callbacks (devices/drivers).
948 *
949 * The caller is responsible for serializing the control operations on the
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 */
955DECLHIDDEN(int) tstDevPdmR3ThreadResume(PPDMTHREAD pThread)
956{
957 /*
958 * Assert sanity.
959 */
960 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
961 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
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;
971 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_RESUMING, PDMTHREADSTATE_SUSPENDED))
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);
980 if ( RT_SUCCESS(rc)
981 && pThread->enmState != PDMTHREADSTATE_RUNNING)
982 rc = VERR_PDM_THREAD_IPE_2;
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.

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