diff options
Diffstat (limited to 'src/VBox/Runtime/common/misc/req.cpp')
-rw-r--r-- | src/VBox/Runtime/common/misc/req.cpp | 476 |
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; +} + |