summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMR3/PDMR3Task.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/VMM/VMMR3/PDMR3Task.cpp
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/VMM/VMMR3/PDMR3Task.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/PDMR3Task.cpp638
1 files changed, 638 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR3/PDMR3Task.cpp b/src/VBox/VMM/VMMR3/PDMR3Task.cpp
new file mode 100644
index 00000000..f4893666
--- /dev/null
+++ b/src/VBox/VMM/VMMR3/PDMR3Task.cpp
@@ -0,0 +1,638 @@
+/* $Id: PDMR3Task.cpp $ */
+/** @file
+ * PDM Task - Asynchronous user mode tasks.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_PDM_TASK
+#include "PDMInternal.h"
+#include <VBox/vmm/pdmtask.h>
+#include <VBox/vmm/mm.h>
+#include <VBox/vmm/vm.h>
+#include <VBox/err.h>
+
+#include <VBox/log.h>
+#include <VBox/sup.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+
+
+/**
+ * @callback_method_impl{FNDBGFINFOARGVINT}
+ */
+static DECLCALLBACK(void) pdmR3TaskInfo(PVM pVM, PCDBGFINFOHLP pHlp, int cArgs, char **papszArgs)
+{
+ RT_NOREF(cArgs, papszArgs); /* for now. */
+
+ uint32_t cSetsDisplayed = 0;
+ for (size_t i = 0; i < RT_ELEMENTS(pVM->pdm.s.apTaskSets); i++)
+ {
+ PPDMTASKSET pTaskSet = pVM->pdm.s.apTaskSets[i];
+ if ( pTaskSet
+ && ( pTaskSet->cAllocated > 0
+ || ASMAtomicReadU64(&pTaskSet->fTriggered)))
+ {
+ if (cSetsDisplayed > 0)
+ pHlp->pfnPrintf(pHlp, "\n");
+ pHlp->pfnPrintf(pHlp,
+ "Task set #%u - handle base %u, pending %#RX64%s%s, running %d, %u of %u allocated:\n"
+ /* 123: triggered internal 0123456789abcdef 0123456789abcdef 0x0000 SomeFunctionName */
+ " Hnd: State Type pfnCallback pvUser Flags Name\n",
+ i, pTaskSet->uHandleBase, ASMAtomicReadU64(&pTaskSet->fTriggered),
+ pTaskSet->fRZEnabled ? " RZ-enabled" : "", pTaskSet->hThread != NIL_RTTHREAD ? "" : " no-thread",
+ (int)ASMAtomicReadU32(&pTaskSet->idxRunning), pTaskSet->cAllocated, RT_ELEMENTS(pTaskSet->aTasks));
+ for (unsigned j = 0; j < RT_ELEMENTS(pTaskSet->aTasks); j++)
+ {
+ PPDMTASK pTask = &pTaskSet->aTasks[j];
+ if (pTask->pvOwner)
+ {
+ const char *pszType;
+ switch (pTask->enmType)
+ {
+ case PDMTASKTYPE_DEV: pszType = " device "; break;
+ case PDMTASKTYPE_DRV: pszType = " driver "; break;
+ case PDMTASKTYPE_USB: pszType = " usbdev "; break;
+ case PDMTASKTYPE_INTERNAL: pszType = "internal"; break;
+ default: pszType = "unknown "; break;
+ }
+ pHlp->pfnPrintf(pHlp, " %3u: %s %s %p %p %#06x %s\n", pTaskSet->uHandleBase + j,
+ ASMBitTest(&pTaskSet->fTriggered, j) ? "triggered"
+ : ASMAtomicReadU32(&pTaskSet->idxRunning) == j ? " running " : " idle ",
+ pszType, pTask->pfnCallback, pTask->pvUser, pTask->fFlags, pTask->pszName);
+ }
+ }
+
+ cSetsDisplayed++;
+ }
+ }
+}
+
+
+/**
+ * Initializes the ring-0 capable tasks during VM construction.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ */
+int pdmR3TaskInit(PVM pVM)
+{
+ for (size_t i = 0; i < RT_ELEMENTS(pVM->pdm.s.aTaskSets); i++)
+ {
+ PPDMTASKSET pTaskSet = &pVM->pdm.s.aTaskSets[i];
+
+ pTaskSet->u32Magic = PDMTASKSET_MAGIC;
+ pTaskSet->fRZEnabled = true;
+ //pTaskSet->cAllocated = 0;
+ pTaskSet->uHandleBase = (uint16_t)(i * RT_ELEMENTS(pTaskSet->aTasks));
+ pTaskSet->hThread = NIL_RTTHREAD;
+ int rc = SUPSemEventCreate(pVM->pSession, &pTaskSet->hEventR0);
+ AssertRCReturn(rc, rc);
+ pTaskSet->hEventR3 = NIL_RTSEMEVENT;
+ //pTaskSet->fTriggered = 0;
+ pTaskSet->idxRunning = UINT8_MAX;
+ //pTaskSet->fShutdown = false;
+ pTaskSet->pVM = pVM;
+
+ pVM->pdm.s.apTaskSets[i] = pTaskSet;
+ }
+
+ int rc = DBGFR3InfoRegisterInternalArgv(pVM, "tasks", "PDM tasks", pdmR3TaskInfo, 0 /*fFlags*/);
+ AssertRC(rc);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Terminates task threads when the VM is destroyed.
+ *
+ * @param pVM The cross context VM structure.
+ */
+void pdmR3TaskTerm(PVM pVM)
+{
+ /*
+ * Signal all the threads first.
+ */
+ for (size_t i = 0; i < RT_ELEMENTS(pVM->pdm.s.apTaskSets); i++)
+ {
+ PPDMTASKSET pTaskSet = pVM->pdm.s.apTaskSets[i];
+ if (pTaskSet)
+ {
+ /*
+ * Set the shutdown indicator and signal the thread.
+ */
+ ASMAtomicWriteBool(&pTaskSet->fShutdown, true);
+
+ if (pTaskSet->hEventR0 != NIL_SUPSEMEVENT)
+ {
+ int rc = SUPSemEventSignal(pVM->pSession, pTaskSet->hEventR0);
+ AssertRC(rc);
+ }
+
+ if (pTaskSet->hEventR3 != NIL_RTSEMEVENT)
+ {
+ int rc = RTSemEventSignal(pTaskSet->hEventR3);
+ AssertRC(rc);
+ }
+ }
+ }
+
+ /*
+ * Wait for them to terminate and clean up semaphores.
+ */
+ for (size_t i = 0; i < RT_ELEMENTS(pVM->pdm.s.apTaskSets); i++)
+ {
+ PPDMTASKSET pTaskSet = pVM->pdm.s.apTaskSets[i];
+ if (pTaskSet)
+ {
+ /*
+ * Wait for the thread to terminate.
+ */
+ if (pTaskSet->hThread != NIL_RTTHREAD)
+ {
+ int rc = RTThreadWait(pTaskSet->hThread, RT_MS_30SEC, NULL);
+ AssertLogRelMsg(RT_SUCCESS(rc), ("pTaskSet %u: thread wait failed: %Rrc\n", i, rc));
+ if (RT_SUCCESS(rc))
+ pTaskSet->hThread = NIL_RTTHREAD;
+ }
+
+ /*
+ * Destroy the semaphore.
+ */
+ if (pTaskSet->hEventR0 != NIL_SUPSEMEVENT)
+ {
+ int rc = SUPSemEventClose(pVM->pSession, pTaskSet->hEventR0);
+ AssertRC(rc);
+ pTaskSet->hEventR0 = NIL_SUPSEMEVENT;
+ }
+
+ if (pTaskSet->hEventR3 != NIL_RTSEMEVENT)
+ {
+ int rc = RTSemEventDestroy(pTaskSet->hEventR3);
+ AssertRC(rc);
+ pTaskSet->hEventR3 = NIL_RTSEMEVENT;
+ }
+ }
+ }
+}
+
+
+/**
+ * @callback_method_impl{FNRTTHREAD,
+ * PDM Asynchronous Task Executor Thread}
+ */
+static DECLCALLBACK(int) pdmR3TaskThread(RTTHREAD ThreadSelf, void *pvUser)
+{
+ PPDMTASKSET const pTaskSet = (PPDMTASKSET)pvUser;
+ AssertPtr(pTaskSet);
+ Assert(pTaskSet->u32Magic == PDMTASKSET_MAGIC);
+ RT_NOREF(ThreadSelf);
+
+ /*
+ * Process stuff until we're told to terminate.
+ */
+ while (!ASMAtomicReadBool(&pTaskSet->fShutdown))
+ {
+ /*
+ * Process pending tasks.
+ *
+ * The outer loop runs till there are no more pending tasks.
+ *
+ * The inner loop takes one snapshot of fTriggered and processes all
+ * pending bits in the snaphot. This ensure fairness.
+ */
+ for (;;)
+ {
+ uint64_t fTriggered = ASMAtomicReadU64(&pTaskSet->fTriggered);
+ unsigned iTask = ASMBitFirstSetU64(fTriggered);
+ if (iTask == 0)
+ break;
+ uint32_t cShutdown = 3;
+ do
+ {
+ iTask--;
+ AssertBreak(iTask < RT_ELEMENTS(pTaskSet->aTasks));
+
+ if (ASMAtomicBitTestAndClear(&pTaskSet->fTriggered, iTask))
+ {
+ PPDMTASK pTask = &pTaskSet->aTasks[iTask];
+
+ /* Copy out the data we need here to try avoid destruction race trouble. */
+ PDMTASKTYPE const enmType = pTask->enmType;
+ PFNRT const pfnCallback = pTask->pfnCallback;
+ void * const pvOwner = pTask->pvOwner;
+ void * const pvTaskUser = pTask->pvUser;
+
+ ASMAtomicWriteU32(&pTaskSet->idxRunning, iTask);
+
+ if ( pvOwner
+ && pfnCallback
+ && pvOwner == pTask->pvOwner
+ && pfnCallback == pTask->pfnCallback
+ && pvTaskUser == pTask->pvUser
+ && enmType == pTask->enmType)
+ {
+ pTask->cRuns += 1;
+ switch (pTask->enmType)
+ {
+ case PDMTASKTYPE_DEV:
+ Log2(("pdmR3TaskThread: Runs dev task %s (%#x)\n", pTask->pszName, iTask + pTaskSet->uHandleBase));
+ ((PFNPDMTASKDEV)(pfnCallback))((PPDMDEVINS)pvOwner, pvTaskUser);
+ break;
+ case PDMTASKTYPE_DRV:
+ Log2(("pdmR3TaskThread: Runs drv task %s (%#x)\n", pTask->pszName, iTask + pTaskSet->uHandleBase));
+ ((PFNPDMTASKDRV)(pfnCallback))((PPDMDRVINS)pvOwner, pvTaskUser);
+ break;
+ case PDMTASKTYPE_USB:
+ Log2(("pdmR3TaskThread: Runs USB task %s (%#x)\n", pTask->pszName, iTask + pTaskSet->uHandleBase));
+ ((PFNPDMTASKUSB)(pfnCallback))((PPDMUSBINS)pvOwner, pvTaskUser);
+ break;
+ case PDMTASKTYPE_INTERNAL:
+ Log2(("pdmR3TaskThread: Runs int task %s (%#x)\n", pTask->pszName, iTask + pTaskSet->uHandleBase));
+ ((PFNPDMTASKINT)(pfnCallback))((PVM)pvOwner, pvTaskUser);
+ break;
+ default:
+ AssertFailed();
+ }
+ }
+ else /* Note! There might be a race here during destruction. */
+ AssertMsgFailed(("%d %p %p %p\n", enmType, pvOwner, pfnCallback, pvTaskUser));
+
+ ASMAtomicWriteU32(&pTaskSet->idxRunning, UINT32_MAX);
+ }
+
+ /* Next pending task. */
+ fTriggered &= ~RT_BIT_64(iTask);
+ iTask = ASMBitFirstSetU64(fTriggered);
+ } while (iTask != 0);
+
+ /*
+ * If we're shutting down, we'll try drain the pending tasks by
+ * looping three more times before just quitting. We don't want
+ * to get stuck here if some stuff is misbehaving.
+ */
+ if (!ASMAtomicReadBool(&pTaskSet->fShutdown))
+ { /* likely */ }
+ else if (--cShutdown == 0)
+ break;
+ }
+
+ /*
+ * Wait unless we're shutting down.
+ */
+ if (!ASMAtomicReadBool(&pTaskSet->fShutdown))
+ {
+ if (pTaskSet->fRZEnabled)
+ SUPSemEventWaitNoResume(pTaskSet->pVM->pSession, pTaskSet->hEventR0, RT_MS_15SEC);
+ else
+ RTSemEventWaitNoResume(pTaskSet->hEventR3, RT_MS_15SEC);
+ }
+ }
+
+ /*
+ * Complain about pending tasks.
+ */
+ uint64_t const fTriggered = ASMAtomicReadU64(&pTaskSet->fTriggered);
+ AssertLogRelMsg(fTriggered == 0, ("fTriggered=%#RX64 - %u %s\n", fTriggered, ASMBitFirstSetU64(fTriggered) - 1,
+ pTaskSet->aTasks[ASMBitFirstSetU64(fTriggered) - 1].pszName));
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for PDMR3TaskCreate().
+ */
+DECLINLINE(PPDMTASK) pdmR3TaskAllocInSet(PPDMTASKSET pTaskSet)
+{
+ if (pTaskSet->cAllocated < RT_ELEMENTS(pTaskSet->aTasks))
+ {
+ for (size_t j = 0; j < RT_ELEMENTS(pTaskSet->aTasks); j++)
+ if (pTaskSet->aTasks[j].pvOwner == NULL)
+ return &pTaskSet->aTasks[j];
+ AssertFailed();
+ }
+ return NULL;
+}
+
+/**
+ * Creates a task.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param fFlags PDMTASK_F_XXX.
+ * @param pszName The task name (function name ++).
+ * @param enmType The task owner type.
+ * @param pvOwner The task owner pointer.
+ * @param pfnCallback The task callback.
+ * @param pvUser The user argument for the callback.
+ * @param phTask Where to return the task handle.
+ * @thread EMT(0)
+ */
+VMMR3_INT_DECL(int) PDMR3TaskCreate(PVM pVM, uint32_t fFlags, const char *pszName, PDMTASKTYPE enmType, void *pvOwner,
+ PFNRT pfnCallback, void *pvUser, PDMTASKHANDLE *phTask)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(!(fFlags & ~PDMTASK_F_VALID_MASK), VERR_INVALID_FLAGS);
+ AssertPtrReturn(pvOwner, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT); /* implicit serialization by requiring EMT(0) */
+ switch (enmType)
+ {
+ case PDMTASKTYPE_DEV:
+ case PDMTASKTYPE_DRV:
+ case PDMTASKTYPE_USB:
+ break;
+ case PDMTASKTYPE_INTERNAL:
+ AssertReturn(pvOwner == (void *)pVM, VERR_INVALID_PARAMETER);
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * If the callback must be ring-0 triggerable, we are restricted to the
+ * task sets living the VM structure. Otherwise, pick from the dynamically
+ * allocated sets living on ring-3 heap.
+ */
+ PPDMTASKSET pTaskSet = NULL;
+ PPDMTASK pTask = NULL;
+ if (fFlags & PDMTASK_F_RZ)
+ {
+ for (size_t i = 0; i < RT_ELEMENTS(pVM->pdm.s.aTaskSets); i++)
+ {
+ pTaskSet = &pVM->pdm.s.aTaskSets[i];
+ pTask = pdmR3TaskAllocInSet(pTaskSet);
+ if (pTask)
+ break;
+ }
+ }
+ else
+ {
+ for (size_t i = RT_ELEMENTS(pVM->pdm.s.aTaskSets); i < RT_ELEMENTS(pVM->pdm.s.apTaskSets); i++)
+ {
+ pTaskSet = pVM->pdm.s.apTaskSets[i];
+ if (pTaskSet)
+ {
+ pTask = pdmR3TaskAllocInSet(pTaskSet);
+ if (pTask)
+ break;
+ }
+ else
+ {
+ /*
+ * Try allocate a new set.
+ */
+ LogFlow(("PDMR3TaskCreate: Allocating new task set (%#u)...\n", i));
+ pTaskSet = (PPDMTASKSET)MMR3HeapAllocZ(pVM, MM_TAG_PDM, sizeof(*pTaskSet));
+ AssertReturn(pTaskSet, VERR_NO_MEMORY);
+
+ pTaskSet->u32Magic = PDMTASKSET_MAGIC;
+ //pTaskSet->fRZEnabled = false;
+ //pTaskSet->cAllocated = 0;
+ pTaskSet->uHandleBase = (uint16_t)(i * RT_ELEMENTS(pTaskSet->aTasks));
+ pTaskSet->hThread = NIL_RTTHREAD;
+ pTaskSet->hEventR0 = NIL_SUPSEMEVENT;
+ int rc = RTSemEventCreate(&pTaskSet->hEventR3);
+ AssertRCReturnStmt(rc, MMR3HeapFree(pTaskSet), rc);
+ //pTaskSet->fTriggered = 0;
+ pTaskSet->idxRunning = UINT8_MAX;
+ //pTaskSet->fShutdown = false;
+ pTaskSet->pVM = pVM;
+
+ pVM->pdm.s.apTaskSets[i] = pTaskSet;
+ pTask = &pTaskSet->aTasks[0];
+ break;
+ }
+ }
+ }
+ AssertLogRelReturn(pTask, VERR_OUT_OF_RESOURCES);
+
+ /*
+ * Do we need to start a worker thread? Do this first as it can fail.
+ */
+ if (pTaskSet->hThread == NIL_RTTHREAD)
+ {
+ int rc = RTThreadCreateF(&pTaskSet->hThread, pdmR3TaskThread, pTaskSet, 0 /*cbStack*/, RTTHREADTYPE_IO,
+ RTTHREADFLAGS_WAITABLE, "TaskSet%u", pTaskSet->uHandleBase / RT_ELEMENTS(pTaskSet->aTasks));
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ /*
+ * Complete the allocation.
+ */
+ pTask->enmType = enmType;
+ pTask->fFlags = fFlags;
+ pTask->pvUser = pvUser;
+ pTask->pfnCallback = pfnCallback;
+ pTask->pszName = pszName;
+ ASMAtomicWritePtr(&pTask->pvOwner, pvOwner);
+ pTaskSet->cAllocated += 1;
+
+ uint32_t const hTask = pTaskSet->uHandleBase + (uint32_t)(pTask - &pTaskSet->aTasks[0]);
+ *phTask = hTask;
+
+ STAMR3RegisterF(pVM, &pTask->cRuns, STAMTYPE_U32_RESET, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
+ "Number of times the task has been executed.", "/PDM/Tasks/%03u-%s-runs", hTask, pszName);
+ STAMR3RegisterF(pVM, (void *)&pTask->cAlreadyTrigged, STAMTYPE_U32_RESET, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
+ "Number of times the task was re-triggered.", "/PDM/Tasks/%03u-%s-retriggered", hTask, pszName);
+
+ LogFlow(("PDMR3TaskCreate: Allocated %u for %s\n", hTask, pszName));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Creates an internal task.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param fFlags PDMTASK_F_XXX.
+ * @param pszName The task name (function name ++).
+ * @param pfnCallback The task callback.
+ * @param pvUser The user argument for the callback.
+ * @param phTask Where to return the task handle.
+ * @thread EMT(0)
+ */
+VMMR3_INT_DECL(int) PDMR3TaskCreateInternal(PVM pVM, uint32_t fFlags, const char *pszName,
+ PFNPDMTASKINT pfnCallback, void *pvUser, PDMTASKHANDLE *phTask)
+{
+ return PDMR3TaskCreate(pVM, fFlags, pszName, PDMTASKTYPE_INTERNAL, pVM, (PFNRT)pfnCallback, pvUser, phTask);
+}
+
+
+/**
+ * Worker for PDMR3TaskDestroyAllByOwner() and PDMR3TaskDestroySpecific().
+ */
+static void pdmR3TaskDestroyOne(PVM pVM, PPDMTASKSET pTaskSet, PPDMTASK pTask, size_t iTask)
+{
+ AssertPtr(pTask->pvOwner);
+
+ /*
+ * Delay if busy.
+ */
+ uint32_t cYields = 64;
+ while ( ASMAtomicReadU32(&pTaskSet->idxRunning) == iTask
+ && cYields > 0
+ && pTaskSet->hThread != NIL_RTTHREAD)
+ {
+ ASMNopPause();
+ RTThreadYield();
+ }
+
+ /*
+ * Zap it (very noisy, but whatever).
+ */
+ LogFlow(("pdmR3TaskDestroyOne: Destroying %zu %s\n", iTask + pTaskSet->uHandleBase, pTask->pszName));
+ AssertPtr(pTask->pvOwner);
+
+ char szPrefix[64];
+ RTStrPrintf(szPrefix, sizeof(szPrefix), "/PDM/Tasks/%03zu-", iTask + pTaskSet->uHandleBase);
+ STAMR3DeregisterByPrefix(pVM->pUVM, szPrefix);
+
+ AssertPtr(pTask->pvOwner);
+ ASMAtomicWriteNullPtr(&pTask->pvOwner);
+ pTask->enmType = (PDMTASKTYPE)0;
+ pTask->fFlags = 0;
+ ASMAtomicWriteNullPtr((void **)&pTask->pfnCallback);
+ ASMAtomicWriteNullPtr(&pTask->pvUser);
+ ASMAtomicWriteNullPtr(&pTask->pszName);
+
+ AssertReturnVoid(pTaskSet->cAllocated > 0);
+ pTaskSet->cAllocated -= 1;
+}
+
+
+/**
+ * Destroys all tasks belonging to @a pvOwner.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param enmType The owner type.
+ * @param pvOwner The owner.
+ */
+VMMR3_INT_DECL(int) PDMR3TaskDestroyAllByOwner(PVM pVM, PDMTASKTYPE enmType, void *pvOwner)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(enmType >= PDMTASKTYPE_DEV && enmType < PDMTASKTYPE_INTERNAL, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pvOwner, VERR_INVALID_POINTER);
+ VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT); /* implicit serialization by requiring EMT(0) */
+
+ /*
+ * Scan all the task sets.
+ */
+ for (size_t i = 0; i < RT_ELEMENTS(pVM->pdm.s.apTaskSets); i++)
+ {
+ PPDMTASKSET pTaskSet = pVM->pdm.s.apTaskSets[i];
+ if (pTaskSet)
+ {
+ ssize_t cLeft = pTaskSet->cAllocated;
+ for (size_t j = 0; j < RT_ELEMENTS(pTaskSet->aTasks) && cLeft > 0; j++)
+ {
+ PPDMTASK pTask = &pTaskSet->aTasks[j];
+ void * const pvTaskOwner = pTask->pvOwner;
+ if (pvTaskOwner)
+ {
+ if ( pvTaskOwner == pvOwner
+ && pTask->enmType == enmType)
+ pdmR3TaskDestroyOne(pVM, pTaskSet, pTask, j);
+ else
+ Assert(pvTaskOwner != pvOwner);
+ cLeft--;
+ }
+ }
+ }
+ else
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Destroys the task @a hTask.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param enmType The owner type.
+ * @param pvOwner The owner.
+ * @param hTask Handle to the task to destroy.
+ */
+VMMR3_INT_DECL(int) PDMR3TaskDestroySpecific(PVM pVM, PDMTASKTYPE enmType, void *pvOwner, PDMTASKHANDLE hTask)
+{
+ /*
+ * Validate the input.
+ */
+ AssertReturn(enmType >= PDMTASKTYPE_DEV && enmType <= PDMTASKTYPE_INTERNAL, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pvOwner, VERR_INVALID_POINTER);
+
+ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
+
+ size_t const iTask = hTask % RT_ELEMENTS(pVM->pdm.s.apTaskSets[0]->aTasks);
+ size_t const iTaskSet = hTask / RT_ELEMENTS(pVM->pdm.s.apTaskSets[0]->aTasks);
+ AssertReturn(iTaskSet < RT_ELEMENTS(pVM->pdm.s.apTaskSets), VERR_INVALID_HANDLE);
+ PPDMTASKSET const pTaskSet = pVM->pdm.s.apTaskSets[iTaskSet];
+ AssertPtrReturn(pTaskSet, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pTaskSet->u32Magic == PDMTASKSET_MAGIC, VERR_INVALID_MAGIC);
+ PPDMTASK const pTask = &pTaskSet->aTasks[iTask];
+
+ VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT); /* implicit serialization by requiring EMT(0) */
+
+ AssertPtrReturn(pTask->pvOwner == pvOwner, VERR_NOT_OWNER);
+ AssertPtrReturn(pTask->enmType == enmType, VERR_NOT_OWNER);
+
+ /*
+ * Do the job.
+ */
+ pdmR3TaskDestroyOne(pVM, pTaskSet, pTask, iTask);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Destroys the internal task @a hTask.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param hTask Handle to the task to destroy.
+ */
+VMMR3_INT_DECL(int) PDMR3TaskDestroyInternal(PVM pVM, PDMTASKHANDLE hTask)
+{
+ return PDMR3TaskDestroySpecific(pVM, PDMTASKTYPE_INTERNAL, pVM, hTask);
+}
+