VirtualBox

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

Last change on this file since 91703 was 83296, checked in by vboxsync, 5 years ago

Devices/tstDevice: Copy over and modify PDMThread* API so we can fuzz saved states of device s which delegate some parts of the saved state loading to workers

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