summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r3/win/fileaio-win.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/Runtime/r3/win/fileaio-win.cpp534
1 files changed, 534 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/win/fileaio-win.cpp b/src/VBox/Runtime/r3/win/fileaio-win.cpp
new file mode 100644
index 00000000..12ffc3c9
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/fileaio-win.cpp
@@ -0,0 +1,534 @@
+/* $Id: fileaio-win.cpp $ */
+/** @file
+ * IPRT - File async I/O, native implementation for the Windows host platform.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+
+#include <iprt/asm.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/fileaio.h"
+
+#include <iprt/win/windows.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Transfer direction.
+ */
+typedef enum TRANSFERDIRECTION
+{
+ TRANSFERDIRECTION_INVALID = 0,
+ /** Read. */
+ TRANSFERDIRECTION_READ,
+ /** Write. */
+ TRANSFERDIRECTION_WRITE,
+ /** The usual 32-bit hack. */
+ TRANSFERDIRECTION_32BIT_HACK = 0x7fffffff
+} TRANSFERDIRECTION;
+
+/**
+ * Async I/O completion context state.
+ */
+typedef struct RTFILEAIOCTXINTERNAL
+{
+ /** handle to I/O completion port. */
+ HANDLE hIoCompletionPort;
+ /** Current number of requests pending. */
+ volatile int32_t cRequests;
+ /** Flag whether the thread was woken up. */
+ volatile bool fWokenUp;
+ /** Flag whether the thread is currently waiting. */
+ volatile bool fWaiting;
+ /** Flags given during creation. */
+ uint32_t fFlags;
+ /** Magic value (RTFILEAIOCTX_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOCTXINTERNAL;
+/** Pointer to an internal context structure. */
+typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL;
+
+/**
+ * Async I/O request state.
+ */
+typedef struct RTFILEAIOREQINTERNAL
+{
+ /** Overlapped structure. */
+ OVERLAPPED Overlapped;
+ /** Current state the request is in. */
+ RTFILEAIOREQSTATE enmState;
+ /** The file handle. */
+ HANDLE hFile;
+ /** Kind of transfer Read/Write. */
+ TRANSFERDIRECTION enmTransferDirection;
+ /** Number of bytes to transfer. */
+ size_t cbTransfer;
+ /** Pointer to the buffer. */
+ void *pvBuf;
+ /** Opaque user data. */
+ void *pvUser;
+ /** Flag whether the request completed. */
+ bool fCompleted;
+ /** Number of bytes transferred successfully. */
+ size_t cbTransfered;
+ /** Error code of the completed request. */
+ int Rc;
+ /** Completion context we are assigned to. */
+ PRTFILEAIOCTXINTERNAL pCtxInt;
+ /** Magic value (RTFILEAIOREQ_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOREQINTERNAL;
+/** Pointer to an internal request structure. */
+typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Id for the wakeup event. */
+#define AIO_CONTEXT_WAKEUP_EVENT 1
+/** Converts a pointer to an OVERLAPPED structure to a internal request. */
+#define OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped) ( (PRTFILEAIOREQINTERNAL)((uintptr_t)(pOverlapped) - RT_UOFFSETOF(RTFILEAIOREQINTERNAL, Overlapped)) )
+
+RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
+{
+ AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
+
+ /* No limits known. */
+ pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS;
+ pAioLimits->cbBufferAlignment = 0;
+
+ return VINF_SUCCESS;
+}
+
+RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
+{
+ AssertPtrReturn(phReq, VERR_INVALID_POINTER);
+
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL));
+ if (RT_UNLIKELY(!pReqInt))
+ return VERR_NO_MEMORY;
+
+ pReqInt->pCtxInt = NULL;
+ pReqInt->fCompleted = false;
+ pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ *phReq = (RTFILEAIOREQ)pReqInt;
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
+{
+ /*
+ * Validate the handle and ignore nil.
+ */
+ if (hReq == NIL_RTFILEAIOREQ)
+ return VINF_SUCCESS;
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+
+ /*
+ * Trash the magic and free it.
+ */
+ ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
+ RTMemFree(pReqInt);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Worker setting up the request.
+ */
+DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
+ TRANSFERDIRECTION enmTransferDirection,
+ RTFOFF off, void *pvBuf, size_t cbTransfer,
+ void *pvUser)
+{
+ /*
+ * Validate the input.
+ */
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ Assert(hFile != NIL_RTFILE);
+ AssertPtr(pvBuf);
+ Assert(off >= 0);
+ Assert(cbTransfer > 0);
+
+ pReqInt->enmTransferDirection = enmTransferDirection;
+ pReqInt->hFile = (HANDLE)RTFileToNative(hFile);
+ pReqInt->Overlapped.Offset = (DWORD)(off & 0xffffffff);
+ pReqInt->Overlapped.OffsetHigh = (DWORD)(off >> 32);
+ pReqInt->cbTransfer = cbTransfer;
+ pReqInt->pvBuf = pvBuf;
+ pReqInt->pvUser = pvUser;
+ pReqInt->fCompleted = false;
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void *pvBuf, size_t cbRead, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_READ,
+ off, pvBuf, cbRead, pvUser);
+}
+
+RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void const *pvBuf, size_t cbWrite, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_WRITE,
+ off, (void *)pvBuf, cbWrite, pvUser);
+}
+
+RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_HANDLE);
+ RT_NOREF_PV(pvUser);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
+
+ return pReqInt->pvUser;
+}
+
+RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+ /**
+ * @todo r=aeichner It is not possible to cancel specific
+ * requests on Windows before Vista.
+ * CancelIo cancels all requests for a file issued by the
+ * calling thread and CancelIoEx which does what we need
+ * is only available from Vista and up.
+ * The solution is to return VERR_FILE_AIO_IN_PROGRESS
+ * if the request didn't completed yet (checked above).
+ * Shouldn't be a big issue because a request is normally
+ * only canceled if it exceeds a timeout which is quite huge.
+ */
+ return VERR_FILE_AIO_COMPLETED;
+}
+
+RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
+{
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+ rc = pReqInt->Rc;
+ if (pcbTransfered && RT_SUCCESS(rc))
+ *pcbTransfered = pReqInt->cbTransfered;
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax, uint32_t fFlags)
+{
+ PRTFILEAIOCTXINTERNAL pCtxInt;
+ AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+ RT_NOREF_PV(cAioReqsMax);
+
+ pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL));
+ if (RT_UNLIKELY(!pCtxInt))
+ return VERR_NO_MEMORY;
+
+ pCtxInt->hIoCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
+ NULL,
+ 0,
+ 0);
+ if (RT_UNLIKELY(!pCtxInt->hIoCompletionPort))
+ {
+ RTMemFree(pCtxInt);
+ return VERR_NO_MEMORY;
+ }
+
+ pCtxInt->fFlags = fFlags;
+ pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
+
+ *phAioCtx = (RTFILEAIOCTX)pCtxInt;
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
+{
+ /* Validate the handle and ignore nil. */
+ if (hAioCtx == NIL_RTFILEAIOCTX)
+ return VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ /* Cannot destroy a busy context. */
+ if (RT_UNLIKELY(pCtxInt->cRequests))
+ return VERR_FILE_AIO_BUSY;
+
+ CloseHandle(pCtxInt->hIoCompletionPort);
+ ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD);
+ RTMemFree(pCtxInt);
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
+{
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ HANDLE hTemp = CreateIoCompletionPort((HANDLE)RTFileToNative(hFile), pCtxInt->hIoCompletionPort, 0, 1);
+ if (hTemp != pCtxInt->hIoCompletionPort)
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+}
+
+RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
+{
+ RT_NOREF_PV(hAioCtx);
+ return RTFILEAIO_UNLIMITED_REQS;
+}
+
+RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
+{
+ /*
+ * Parameter validation.
+ */
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
+ Assert(cReqs <= INT32_MAX);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ size_t i;
+
+ for (i = 0; i < cReqs; i++)
+ {
+ PRTFILEAIOREQINTERNAL pReqInt = pahReqs[i];
+ BOOL fSucceeded;
+
+ Assert(pReqInt->cbTransfer == (DWORD)pReqInt->cbTransfer);
+ if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_READ)
+ {
+ fSucceeded = ReadFile(pReqInt->hFile, pReqInt->pvBuf,
+ (DWORD)pReqInt->cbTransfer, NULL,
+ &pReqInt->Overlapped);
+ }
+ else if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_WRITE)
+ {
+ fSucceeded = WriteFile(pReqInt->hFile, pReqInt->pvBuf,
+ (DWORD)pReqInt->cbTransfer, NULL,
+ &pReqInt->Overlapped);
+ }
+ else
+ {
+ fSucceeded = false;
+ AssertMsgFailed(("Invalid transfer direction\n"));
+ }
+
+ if (RT_UNLIKELY(!fSucceeded && GetLastError() != ERROR_IO_PENDING))
+ {
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ rc = RTErrConvertFromWin32(GetLastError());
+ pReqInt->Rc = rc;
+ break;
+ }
+ RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
+ }
+
+ ASMAtomicAddS32(&pCtxInt->cRequests, (int32_t)i);
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
+ PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
+{
+ /*
+ * Validate the parameters, making sure to always set pcReqs.
+ */
+ AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
+ *pcReqs = 0; /* always set */
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
+
+ /*
+ * Can't wait if there are no requests around.
+ */
+ if ( RT_UNLIKELY(ASMAtomicUoReadS32(&pCtxInt->cRequests) == 0)
+ && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS))
+ return VERR_FILE_AIO_NO_REQUEST;
+
+ /* Wait for at least one. */
+ if (!cMinReqs)
+ cMinReqs = 1;
+
+ /*
+ * Loop until we're woken up, hit an error (incl timeout), or
+ * have collected the desired number of requests.
+ */
+ int rc = VINF_SUCCESS;
+ int cRequestsCompleted = 0;
+ while ( !pCtxInt->fWokenUp
+ && cMinReqs > 0)
+ {
+ uint64_t StartNanoTS = 0;
+ DWORD dwTimeout = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies;
+ DWORD cbTransfered;
+ LPOVERLAPPED pOverlapped;
+ ULONG_PTR lCompletionKey;
+ BOOL fSucceeded;
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ StartNanoTS = RTTimeNanoTS();
+
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, true);
+ fSucceeded = GetQueuedCompletionStatus(pCtxInt->hIoCompletionPort,
+ &cbTransfered,
+ &lCompletionKey,
+ &pOverlapped,
+ dwTimeout);
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, false);
+ if ( !fSucceeded
+ && !pOverlapped)
+ {
+ /* The call failed to dequeue a completion packet, includes VERR_TIMEOUT */
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+ }
+
+ /* Check if we got woken up. */
+ if (lCompletionKey == AIO_CONTEXT_WAKEUP_EVENT)
+ {
+ Assert(fSucceeded && !pOverlapped);
+ break;
+ }
+
+ /* A request completed. */
+ PRTFILEAIOREQINTERNAL pReqInt = OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped);
+ AssertPtr(pReqInt);
+ Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
+
+ /* Mark the request as finished. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ pReqInt->cbTransfered = cbTransfered;
+ if (fSucceeded)
+ pReqInt->Rc = VINF_SUCCESS;
+ else
+ {
+ DWORD errCode = GetLastError();
+ pReqInt->Rc = RTErrConvertFromWin32(errCode);
+ if (pReqInt->Rc == VERR_UNRESOLVED_ERROR)
+ LogRel(("AIO/win: Request %#p returned rc=%Rrc (native %u\n)", pReqInt, pReqInt->Rc, errCode));
+ }
+
+ pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt;
+
+ /* Update counter. */
+ cMinReqs--;
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ /* Recalculate timeout. */
+ uint64_t NanoTS = RTTimeNanoTS();
+ uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
+ if (cMilliesElapsed < cMillies)
+ cMillies -= cMilliesElapsed;
+ else
+ cMillies = 0;
+ }
+ }
+
+ /*
+ * Update the context state and set the return value.
+ */
+ *pcReqs = cRequestsCompleted;
+ ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
+
+ /*
+ * Clear the wakeup flag and set rc.
+ */
+ bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, false);
+
+ if ( fWokenUp
+ && RT_SUCCESS(rc))
+ rc = VERR_INTERRUPTED;
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
+{
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true);
+ bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting);
+
+ if ( !fWokenUp
+ && fWaiting)
+ {
+ BOOL fSucceeded = PostQueuedCompletionStatus(pCtxInt->hIoCompletionPort,
+ 0, AIO_CONTEXT_WAKEUP_EVENT,
+ NULL);
+
+ if (!fSucceeded)
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+
+ return rc;
+}
+