summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/misc/req.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/misc/req.cpp')
-rw-r--r--src/VBox/Runtime/common/misc/req.cpp476
1 files changed, 476 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/misc/req.cpp b/src/VBox/Runtime/common/misc/req.cpp
new file mode 100644
index 00000000..2d965399
--- /dev/null
+++ b/src/VBox/Runtime/common/misc/req.cpp
@@ -0,0 +1,476 @@
+/* $Id: req.cpp $ */
+/** @file
+ * IPRT - Request packets
+ */
+
+/*
+ * Copyright (C) 2006-2020 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/req.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+
+#include "internal/req.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+/**
+ * Allocate a new request from the heap.
+ *
+ * @returns IPRT status code.
+ * @param enmType The reques type.
+ * @param fPoolOrQueue The owner type.
+ * @param pvOwner The owner.
+ * @param phReq Where to return the request handle.
+ */
+DECLHIDDEN(int) rtReqAlloc(RTREQTYPE enmType, bool fPoolOrQueue, void *pvOwner, PRTREQ *phReq)
+{
+ PRTREQ pReq = (PRTREQ)RTMemAllocZ(sizeof(*pReq));
+ if (RT_UNLIKELY(!pReq))
+ return VERR_NO_MEMORY;
+
+ /*
+ * Create the semaphore used for waiting.
+ */
+ int rc = RTSemEventCreate(&pReq->EventSem);
+ AssertRCReturnStmt(rc, RTMemFree(pReq), rc);
+
+ /*
+ * Initialize the packet and return it.
+ */
+ pReq->u32Magic = RTREQ_MAGIC;
+ pReq->fEventSemClear = true;
+ pReq->fSignalPushBack = true;
+ pReq->fPoolOrQueue = fPoolOrQueue;
+ pReq->iStatusX = VERR_RT_REQUEST_STATUS_STILL_PENDING;
+ pReq->enmState = RTREQSTATE_ALLOCATED;
+ pReq->pNext = NULL;
+ pReq->uOwner.pv = pvOwner;
+ pReq->fFlags = RTREQFLAGS_IPRT_STATUS;
+ pReq->enmType = enmType;
+ pReq->cRefs = 1;
+
+ *phReq = pReq;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Re-initializes a request when it's being recycled.
+ *
+ * @returns IRPT status code, the request is freed on failure.
+ * @param pReq The request.
+ * @param enmType The request type.
+ */
+DECLHIDDEN(int) rtReqReInit(PRTREQINT pReq, RTREQTYPE enmType)
+{
+ Assert(pReq->u32Magic == RTREQ_MAGIC);
+ Assert(pReq->enmType == RTREQTYPE_INVALID);
+ Assert(pReq->enmState == RTREQSTATE_FREE);
+ Assert(pReq->cRefs == 0);
+
+ /*
+ * Make sure the event sem is not signaled.
+ */
+ if (!pReq->fEventSemClear)
+ {
+ int rc = RTSemEventWait(pReq->EventSem, 0);
+ if (rc != VINF_SUCCESS && rc != VERR_TIMEOUT)
+ {
+ /*
+ * This shall not happen, but if it does we'll just destroy
+ * the semaphore and create a new one.
+ */
+ AssertMsgFailed(("rc=%Rrc from RTSemEventWait(%#x).\n", rc, pReq->EventSem));
+ RTSemEventDestroy(pReq->EventSem);
+ rc = RTSemEventCreate(&pReq->EventSem);
+ if (RT_FAILURE(rc))
+ {
+ AssertRC(rc);
+ pReq->EventSem = NIL_RTSEMEVENT;
+ rtReqFreeIt(pReq);
+ return rc;
+ }
+ }
+ pReq->fEventSemClear = true;
+ }
+ else
+ Assert(RTSemEventWait(pReq->EventSem, 0) == VERR_TIMEOUT);
+
+ /*
+ * Initialize the packet and return it.
+ */
+ ASMAtomicWriteNullPtr(&pReq->pNext);
+ pReq->iStatusX = VERR_RT_REQUEST_STATUS_STILL_PENDING;
+ pReq->enmState = RTREQSTATE_ALLOCATED;
+ pReq->fFlags = RTREQFLAGS_IPRT_STATUS;
+ pReq->enmType = enmType;
+ pReq->cRefs = 1;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(uint32_t) RTReqRetain(PRTREQ hReq)
+{
+ PRTREQINT pReq = hReq;
+ AssertPtrReturn(pReq, UINT32_MAX);
+ AssertReturn(pReq->u32Magic == RTREQ_MAGIC, UINT32_MAX);
+
+ return ASMAtomicIncU32(&pReq->cRefs);
+}
+RT_EXPORT_SYMBOL(RTReqRetain);
+
+
+/**
+ * Frees a request.
+ *
+ * @param pReq The request.
+ */
+DECLHIDDEN(void) rtReqFreeIt(PRTREQINT pReq)
+{
+ Assert(pReq->u32Magic == RTREQ_MAGIC);
+ Assert(pReq->cRefs == 0);
+
+ pReq->u32Magic = RTREQ_MAGIC_DEAD;
+ RTSemEventDestroy(pReq->EventSem);
+ pReq->EventSem = NIL_RTSEMEVENT;
+ RTSemEventMultiDestroy(pReq->hPushBackEvt);
+ pReq->hPushBackEvt = NIL_RTSEMEVENTMULTI;
+ RTMemFree(pReq);
+}
+
+
+RTDECL(uint32_t) RTReqRelease(PRTREQ hReq)
+{
+ /*
+ * Ignore NULL and validate the request.
+ */
+ if (!hReq)
+ return 0;
+ PRTREQINT pReq = hReq;
+ AssertPtrReturn(pReq, UINT32_MAX);
+ AssertReturn(pReq->u32Magic == RTREQ_MAGIC, UINT32_MAX);
+
+ /*
+ * Drop a reference, recycle the request when we reach 0.
+ */
+ uint32_t cRefs = ASMAtomicDecU32(&pReq->cRefs);
+ if (cRefs == 0)
+ {
+ /*
+ * Check packet state.
+ */
+ switch (pReq->enmState)
+ {
+ case RTREQSTATE_ALLOCATED:
+ case RTREQSTATE_COMPLETED:
+ break;
+ default:
+ AssertMsgFailed(("Invalid state %d!\n", pReq->enmState));
+ return 0;
+ }
+
+ /*
+ * Make it a free packet and put it into one of the free packet lists.
+ */
+ pReq->enmState = RTREQSTATE_FREE;
+ pReq->iStatusX = VERR_RT_REQUEST_STATUS_FREED;
+ pReq->enmType = RTREQTYPE_INVALID;
+
+ bool fRecycled;
+ if (pReq->fPoolOrQueue)
+ fRecycled = rtReqPoolRecycle(pReq->uOwner.hPool, pReq);
+ else
+ fRecycled = rtReqQueueRecycle(pReq->uOwner.hQueue, pReq);
+ if (!fRecycled)
+ rtReqFreeIt(pReq);
+ }
+
+ return cRefs;
+}
+RT_EXPORT_SYMBOL(RTReqRelease);
+
+
+RTDECL(int) RTReqSubmit(PRTREQ hReq, RTMSINTERVAL cMillies)
+{
+ LogFlow(("RTReqSubmit: hReq=%p cMillies=%d\n", hReq, cMillies));
+
+ /*
+ * Verify the supplied package.
+ */
+ PRTREQINT pReq = hReq;
+ AssertPtrReturn(pReq, VERR_INVALID_HANDLE);
+ AssertReturn(pReq->u32Magic == RTREQ_MAGIC, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pReq->enmState == RTREQSTATE_ALLOCATED, ("%d\n", pReq->enmState), VERR_RT_REQUEST_STATE);
+ AssertMsgReturn(pReq->uOwner.hQueue && !pReq->pNext && pReq->EventSem != NIL_RTSEMEVENT,
+ ("Invalid request package! Anyone cooking their own packages???\n"),
+ VERR_RT_REQUEST_INVALID_PACKAGE);
+ AssertMsgReturn(pReq->enmType > RTREQTYPE_INVALID && pReq->enmType < RTREQTYPE_MAX,
+ ("Invalid package type %d valid range %d-%d inclusively. This was verified on alloc too...\n",
+ pReq->enmType, RTREQTYPE_INVALID + 1, RTREQTYPE_MAX - 1),
+ VERR_RT_REQUEST_INVALID_TYPE);
+
+ /*
+ * Insert it. Donate the caller's reference if RTREQFLAGS_NO_WAIT is set,
+ * otherwise retain another reference for the queue.
+ */
+ pReq->uSubmitNanoTs = RTTimeNanoTS();
+ pReq->enmState = RTREQSTATE_QUEUED;
+ unsigned fFlags = ((RTREQ volatile *)pReq)->fFlags; /* volatile paranoia */
+ if (!(fFlags & RTREQFLAGS_NO_WAIT))
+ RTReqRetain(pReq);
+
+ if (!pReq->fPoolOrQueue)
+ rtReqQueueSubmit(pReq->uOwner.hQueue, pReq);
+ else
+ rtReqPoolSubmit(pReq->uOwner.hPool, pReq);
+
+ /*
+ * Wait and return.
+ */
+ int rc = VINF_SUCCESS;
+ if (!(fFlags & RTREQFLAGS_NO_WAIT))
+ rc = RTReqWait(pReq, cMillies);
+
+ LogFlow(("RTReqSubmit: returns %Rrc\n", rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTReqSubmit);
+
+
+RTDECL(int) RTReqWait(PRTREQ hReq, RTMSINTERVAL cMillies)
+{
+ LogFlow(("RTReqWait: hReq=%p cMillies=%d\n", hReq, cMillies));
+
+ /*
+ * Verify the supplied package.
+ */
+ PRTREQINT pReq = hReq;
+ AssertPtrReturn(pReq, VERR_INVALID_HANDLE);
+ AssertReturn(pReq->u32Magic == RTREQ_MAGIC, VERR_INVALID_HANDLE);
+ AssertMsgReturn( pReq->enmState == RTREQSTATE_QUEUED
+ || pReq->enmState == RTREQSTATE_PROCESSING
+ || pReq->enmState == RTREQSTATE_COMPLETED,
+ ("Invalid state %d\n", pReq->enmState),
+ VERR_RT_REQUEST_STATE);
+ AssertMsgReturn(pReq->uOwner.hQueue && pReq->EventSem != NIL_RTSEMEVENT,
+ ("Invalid request package! Anyone cooking their own packages???\n"),
+ VERR_RT_REQUEST_INVALID_PACKAGE);
+ AssertMsgReturn(pReq->enmType > RTREQTYPE_INVALID && pReq->enmType < RTREQTYPE_MAX,
+ ("Invalid package type %d valid range %d-%d inclusively. This was verified on alloc too...\n",
+ pReq->enmType, RTREQTYPE_INVALID + 1, RTREQTYPE_MAX - 1),
+ VERR_RT_REQUEST_INVALID_TYPE);
+
+ /*
+ * Wait on the package.
+ */
+ int rc;
+ if (cMillies != RT_INDEFINITE_WAIT)
+ rc = RTSemEventWait(pReq->EventSem, cMillies);
+ else
+ {
+ do
+ {
+ rc = RTSemEventWait(pReq->EventSem, RT_INDEFINITE_WAIT);
+ Assert(rc != VERR_TIMEOUT);
+ } while (pReq->enmState != RTREQSTATE_COMPLETED);
+ }
+ if (rc == VINF_SUCCESS)
+ ASMAtomicXchgSize(&pReq->fEventSemClear, true);
+ if (pReq->enmState == RTREQSTATE_COMPLETED)
+ rc = VINF_SUCCESS;
+ LogFlow(("RTReqWait: returns %Rrc\n", rc));
+ Assert(rc != VERR_INTERRUPTED);
+ Assert(pReq->cRefs >= 1);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTReqWait);
+
+
+RTDECL(int) RTReqGetStatus(PRTREQ hReq)
+{
+ PRTREQINT pReq = hReq;
+ AssertPtrReturn(pReq, VERR_INVALID_POINTER);
+ AssertReturn(pReq->u32Magic == RTREQ_MAGIC, VERR_INVALID_POINTER);
+ return pReq->iStatusX;
+}
+RT_EXPORT_SYMBOL(RTReqGetStatus);
+
+
+
+/**
+ * Process one request.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pReq Request packet to process.
+ */
+DECLHIDDEN(int) rtReqProcessOne(PRTREQINT pReq)
+{
+ LogFlow(("rtReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
+
+ /*
+ * Process the request.
+ */
+ Assert(pReq->enmState == RTREQSTATE_QUEUED);
+ pReq->enmState = RTREQSTATE_PROCESSING;
+ int rcRet = VINF_SUCCESS; /* the return code of this function. */
+ int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
+ switch (pReq->enmType)
+ {
+ /*
+ * A packed down call frame.
+ */
+ case RTREQTYPE_INTERNAL:
+ {
+ uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
+ union
+ {
+ PFNRT pfn;
+ DECLCALLBACKMEMBER(int, pfn00)(void);
+ DECLCALLBACKMEMBER(int, pfn01)(uintptr_t);
+ DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t);
+ DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t);
+ DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ DECLCALLBACKMEMBER(int, pfn11)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ DECLCALLBACKMEMBER(int, pfn12)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ } u;
+ u.pfn = pReq->u.Internal.pfn;
+#ifndef RT_ARCH_X86
+ switch (pReq->u.Internal.cArgs)
+ {
+ case 0: rcRet = u.pfn00(); break;
+ case 1: rcRet = u.pfn01(pauArgs[0]); break;
+ case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
+ case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
+ case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
+ case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
+ case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
+ case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
+ case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
+ case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
+ case 10: rcRet = u.pfn10(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9]); break;
+ case 11: rcRet = u.pfn11(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9], pauArgs[10]); break;
+ case 12: rcRet = u.pfn12(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9], pauArgs[10], pauArgs[11]); break;
+ default:
+ AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
+ rcRet = rcReq = VERR_INTERNAL_ERROR;
+ break;
+ }
+#else /* RT_ARCH_X86 */
+ size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
+# ifdef __GNUC__
+ __asm__ __volatile__("movl %%esp, %%edx\n\t"
+ "subl %2, %%esp\n\t"
+ "andl $0xfffffff0, %%esp\n\t"
+ "shrl $2, %2\n\t"
+ "movl %%esp, %%edi\n\t"
+ "rep movsl\n\t"
+ "movl %%edx, %%edi\n\t"
+ "call *%%eax\n\t"
+ "mov %%edi, %%esp\n\t"
+ : "=a" (rcRet),
+ "=S" (pauArgs),
+ "=c" (cbArgs)
+ : "0" (u.pfn),
+ "1" (pauArgs),
+ "2" (cbArgs)
+ : "edi", "edx");
+# else
+ __asm
+ {
+ xor edx, edx /* just mess it up. */
+ mov eax, u.pfn
+ mov ecx, cbArgs
+ shr ecx, 2
+ mov esi, pauArgs
+ mov ebx, esp
+ sub esp, cbArgs
+ and esp, 0xfffffff0
+ mov edi, esp
+ rep movsd
+ call eax
+ mov esp, ebx
+ mov rcRet, eax
+ }
+# endif
+#endif /* RT_ARCH_X86 */
+ if ((pReq->fFlags & (RTREQFLAGS_RETURN_MASK)) == RTREQFLAGS_VOID)
+ rcRet = VINF_SUCCESS;
+ rcReq = rcRet;
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
+ rcReq = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ /*
+ * Complete the request and then release our request handle reference.
+ */
+ pReq->iStatusX = rcReq;
+ pReq->enmState = RTREQSTATE_COMPLETED;
+ if (pReq->fFlags & RTREQFLAGS_NO_WAIT)
+ LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc (no wait)\n",
+ pReq, rcReq, rcRet));
+ else
+ {
+ /* Notify the waiting thread. */
+ LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - notifying waiting thread\n",
+ pReq, rcReq, rcRet));
+ ASMAtomicXchgSize(&pReq->fEventSemClear, false);
+ int rc2 = RTSemEventSignal(pReq->EventSem);
+ if (rc2 != VINF_SUCCESS)
+ {
+ AssertRC(rc2);
+ rcRet = rc2;
+ }
+ }
+ RTReqRelease(pReq);
+ return rcRet;
+}
+