VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/fileaio-win.cpp@ 96407

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.1 KB
Line 
1/* $Id: fileaio-win.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT - File async I/O, native implementation for the Windows host platform.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_DIR
42
43#include <iprt/asm.h>
44#include <iprt/file.h>
45#include <iprt/mem.h>
46#include <iprt/assert.h>
47#include <iprt/string.h>
48#include <iprt/err.h>
49#include <iprt/log.h>
50#include "internal/fileaio.h"
51
52#include <iprt/win/windows.h>
53
54
55/*********************************************************************************************************************************
56* Structures and Typedefs *
57*********************************************************************************************************************************/
58
59/**
60 * Transfer direction.
61 */
62typedef enum TRANSFERDIRECTION
63{
64 TRANSFERDIRECTION_INVALID = 0,
65 /** Read. */
66 TRANSFERDIRECTION_READ,
67 /** Write. */
68 TRANSFERDIRECTION_WRITE,
69 /** The usual 32-bit hack. */
70 TRANSFERDIRECTION_32BIT_HACK = 0x7fffffff
71} TRANSFERDIRECTION;
72
73/**
74 * Async I/O completion context state.
75 */
76typedef struct RTFILEAIOCTXINTERNAL
77{
78 /** handle to I/O completion port. */
79 HANDLE hIoCompletionPort;
80 /** Current number of requests pending. */
81 volatile int32_t cRequests;
82 /** Flag whether the thread was woken up. */
83 volatile bool fWokenUp;
84 /** Flag whether the thread is currently waiting. */
85 volatile bool fWaiting;
86 /** Flags given during creation. */
87 uint32_t fFlags;
88 /** Magic value (RTFILEAIOCTX_MAGIC). */
89 uint32_t u32Magic;
90} RTFILEAIOCTXINTERNAL;
91/** Pointer to an internal context structure. */
92typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL;
93
94/**
95 * Async I/O request state.
96 */
97typedef struct RTFILEAIOREQINTERNAL
98{
99 /** Overlapped structure. */
100 OVERLAPPED Overlapped;
101 /** Current state the request is in. */
102 RTFILEAIOREQSTATE enmState;
103 /** The file handle. */
104 HANDLE hFile;
105 /** Kind of transfer Read/Write. */
106 TRANSFERDIRECTION enmTransferDirection;
107 /** Number of bytes to transfer. */
108 size_t cbTransfer;
109 /** Pointer to the buffer. */
110 void *pvBuf;
111 /** Opaque user data. */
112 void *pvUser;
113 /** Flag whether the request completed. */
114 bool fCompleted;
115 /** Number of bytes transferred successfully. */
116 size_t cbTransfered;
117 /** Error code of the completed request. */
118 int Rc;
119 /** Completion context we are assigned to. */
120 PRTFILEAIOCTXINTERNAL pCtxInt;
121 /** Magic value (RTFILEAIOREQ_MAGIC). */
122 uint32_t u32Magic;
123} RTFILEAIOREQINTERNAL;
124/** Pointer to an internal request structure. */
125typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL;
126
127
128/*********************************************************************************************************************************
129* Defined Constants And Macros *
130*********************************************************************************************************************************/
131/** Id for the wakeup event. */
132#define AIO_CONTEXT_WAKEUP_EVENT 1
133/** Converts a pointer to an OVERLAPPED structure to a internal request. */
134#define OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped) ( (PRTFILEAIOREQINTERNAL)((uintptr_t)(pOverlapped) - RT_UOFFSETOF(RTFILEAIOREQINTERNAL, Overlapped)) )
135
136RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
137{
138 AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
139
140 /* No limits known. */
141 pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS;
142 pAioLimits->cbBufferAlignment = 0;
143
144 return VINF_SUCCESS;
145}
146
147RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
148{
149 AssertPtrReturn(phReq, VERR_INVALID_POINTER);
150
151 PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL));
152 if (RT_UNLIKELY(!pReqInt))
153 return VERR_NO_MEMORY;
154
155 pReqInt->pCtxInt = NULL;
156 pReqInt->fCompleted = false;
157 pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
158 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
159
160 *phReq = (RTFILEAIOREQ)pReqInt;
161
162 return VINF_SUCCESS;
163}
164
165RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
166{
167 /*
168 * Validate the handle and ignore nil.
169 */
170 if (hReq == NIL_RTFILEAIOREQ)
171 return VINF_SUCCESS;
172 PRTFILEAIOREQINTERNAL pReqInt = hReq;
173 RTFILEAIOREQ_VALID_RETURN(pReqInt);
174 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
175
176 /*
177 * Trash the magic and free it.
178 */
179 ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
180 RTMemFree(pReqInt);
181 return VINF_SUCCESS;
182}
183
184/**
185 * Worker setting up the request.
186 */
187DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
188 TRANSFERDIRECTION enmTransferDirection,
189 RTFOFF off, void *pvBuf, size_t cbTransfer,
190 void *pvUser)
191{
192 /*
193 * Validate the input.
194 */
195 PRTFILEAIOREQINTERNAL pReqInt = hReq;
196 RTFILEAIOREQ_VALID_RETURN(pReqInt);
197 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
198 Assert(hFile != NIL_RTFILE);
199 AssertPtr(pvBuf);
200 Assert(off >= 0);
201 Assert(cbTransfer > 0);
202
203 pReqInt->enmTransferDirection = enmTransferDirection;
204 pReqInt->hFile = (HANDLE)RTFileToNative(hFile);
205 pReqInt->Overlapped.Offset = (DWORD)(off & 0xffffffff);
206 pReqInt->Overlapped.OffsetHigh = (DWORD)(off >> 32);
207 pReqInt->cbTransfer = cbTransfer;
208 pReqInt->pvBuf = pvBuf;
209 pReqInt->pvUser = pvUser;
210 pReqInt->fCompleted = false;
211
212 return VINF_SUCCESS;
213}
214
215RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
216 void *pvBuf, size_t cbRead, void *pvUser)
217{
218 return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_READ,
219 off, pvBuf, cbRead, pvUser);
220}
221
222RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
223 void const *pvBuf, size_t cbWrite, void *pvUser)
224{
225 return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_WRITE,
226 off, (void *)pvBuf, cbWrite, pvUser);
227}
228
229RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
230{
231 PRTFILEAIOREQINTERNAL pReqInt = hReq;
232 RTFILEAIOREQ_VALID_RETURN(pReqInt);
233 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
234 AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_HANDLE);
235 RT_NOREF_PV(pvUser);
236
237 return VERR_NOT_SUPPORTED;
238}
239
240RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
241{
242 PRTFILEAIOREQINTERNAL pReqInt = hReq;
243 RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
244
245 return pReqInt->pvUser;
246}
247
248RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
249{
250 PRTFILEAIOREQINTERNAL pReqInt = hReq;
251 RTFILEAIOREQ_VALID_RETURN(pReqInt);
252 RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
253
254 /**
255 * @todo r=aeichner It is not possible to cancel specific
256 * requests on Windows before Vista.
257 * CancelIo cancels all requests for a file issued by the
258 * calling thread and CancelIoEx which does what we need
259 * is only available from Vista and up.
260 * The solution is to return VERR_FILE_AIO_IN_PROGRESS
261 * if the request didn't completed yet (checked above).
262 * Shouldn't be a big issue because a request is normally
263 * only canceled if it exceeds a timeout which is quite huge.
264 */
265 return VERR_FILE_AIO_COMPLETED;
266}
267
268RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
269{
270 int rc = VINF_SUCCESS;
271 PRTFILEAIOREQINTERNAL pReqInt = hReq;
272 RTFILEAIOREQ_VALID_RETURN(pReqInt);
273 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
274 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
275
276 rc = pReqInt->Rc;
277 if (pcbTransfered && RT_SUCCESS(rc))
278 *pcbTransfered = pReqInt->cbTransfered;
279
280 return rc;
281}
282
283RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax, uint32_t fFlags)
284{
285 PRTFILEAIOCTXINTERNAL pCtxInt;
286 AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
287 AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
288 RT_NOREF_PV(cAioReqsMax);
289
290 pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL));
291 if (RT_UNLIKELY(!pCtxInt))
292 return VERR_NO_MEMORY;
293
294 pCtxInt->hIoCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
295 NULL,
296 0,
297 0);
298 if (RT_UNLIKELY(!pCtxInt->hIoCompletionPort))
299 {
300 RTMemFree(pCtxInt);
301 return VERR_NO_MEMORY;
302 }
303
304 pCtxInt->fFlags = fFlags;
305 pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
306
307 *phAioCtx = (RTFILEAIOCTX)pCtxInt;
308
309 return VINF_SUCCESS;
310}
311
312RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
313{
314 /* Validate the handle and ignore nil. */
315 if (hAioCtx == NIL_RTFILEAIOCTX)
316 return VINF_SUCCESS;
317 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
318 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
319
320 /* Cannot destroy a busy context. */
321 if (RT_UNLIKELY(pCtxInt->cRequests))
322 return VERR_FILE_AIO_BUSY;
323
324 CloseHandle(pCtxInt->hIoCompletionPort);
325 ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD);
326 RTMemFree(pCtxInt);
327
328 return VINF_SUCCESS;
329}
330
331RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
332{
333 int rc = VINF_SUCCESS;
334 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
335 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
336
337 HANDLE hTemp = CreateIoCompletionPort((HANDLE)RTFileToNative(hFile), pCtxInt->hIoCompletionPort, 0, 1);
338 if (hTemp != pCtxInt->hIoCompletionPort)
339 rc = RTErrConvertFromWin32(GetLastError());
340
341 return rc;
342}
343
344RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
345{
346 RT_NOREF_PV(hAioCtx);
347 return RTFILEAIO_UNLIMITED_REQS;
348}
349
350RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
351{
352 /*
353 * Parameter validation.
354 */
355 int rc = VINF_SUCCESS;
356 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
357 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
358 AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
359 Assert(cReqs <= INT32_MAX);
360 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
361 size_t i;
362
363 for (i = 0; i < cReqs; i++)
364 {
365 PRTFILEAIOREQINTERNAL pReqInt = pahReqs[i];
366 BOOL fSucceeded;
367
368 Assert(pReqInt->cbTransfer == (DWORD)pReqInt->cbTransfer);
369 if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_READ)
370 {
371 fSucceeded = ReadFile(pReqInt->hFile, pReqInt->pvBuf,
372 (DWORD)pReqInt->cbTransfer, NULL,
373 &pReqInt->Overlapped);
374 }
375 else if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_WRITE)
376 {
377 fSucceeded = WriteFile(pReqInt->hFile, pReqInt->pvBuf,
378 (DWORD)pReqInt->cbTransfer, NULL,
379 &pReqInt->Overlapped);
380 }
381 else
382 {
383 fSucceeded = false;
384 AssertMsgFailed(("Invalid transfer direction\n"));
385 }
386
387 if (RT_UNLIKELY(!fSucceeded && GetLastError() != ERROR_IO_PENDING))
388 {
389 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
390 rc = RTErrConvertFromWin32(GetLastError());
391 pReqInt->Rc = rc;
392 break;
393 }
394 RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
395 }
396
397 ASMAtomicAddS32(&pCtxInt->cRequests, (int32_t)i);
398
399 return rc;
400}
401
402RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
403 PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
404{
405 /*
406 * Validate the parameters, making sure to always set pcReqs.
407 */
408 AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
409 *pcReqs = 0; /* always set */
410 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
411 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
412 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
413 AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
414 AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
415
416 /*
417 * Can't wait if there are no requests around.
418 */
419 if ( RT_UNLIKELY(ASMAtomicUoReadS32(&pCtxInt->cRequests) == 0)
420 && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS))
421 return VERR_FILE_AIO_NO_REQUEST;
422
423 /* Wait for at least one. */
424 if (!cMinReqs)
425 cMinReqs = 1;
426
427 /*
428 * Loop until we're woken up, hit an error (incl timeout), or
429 * have collected the desired number of requests.
430 */
431 int rc = VINF_SUCCESS;
432 int cRequestsCompleted = 0;
433 while ( !pCtxInt->fWokenUp
434 && cMinReqs > 0)
435 {
436 uint64_t StartNanoTS = 0;
437 DWORD dwTimeout = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies;
438 DWORD cbTransfered;
439 LPOVERLAPPED pOverlapped;
440 ULONG_PTR lCompletionKey;
441 BOOL fSucceeded;
442
443 if (cMillies != RT_INDEFINITE_WAIT)
444 StartNanoTS = RTTimeNanoTS();
445
446 ASMAtomicXchgBool(&pCtxInt->fWaiting, true);
447 fSucceeded = GetQueuedCompletionStatus(pCtxInt->hIoCompletionPort,
448 &cbTransfered,
449 &lCompletionKey,
450 &pOverlapped,
451 dwTimeout);
452 ASMAtomicXchgBool(&pCtxInt->fWaiting, false);
453 if ( !fSucceeded
454 && !pOverlapped)
455 {
456 /* The call failed to dequeue a completion packet, includes VERR_TIMEOUT */
457 rc = RTErrConvertFromWin32(GetLastError());
458 break;
459 }
460
461 /* Check if we got woken up. */
462 if (lCompletionKey == AIO_CONTEXT_WAKEUP_EVENT)
463 {
464 Assert(fSucceeded && !pOverlapped);
465 break;
466 }
467
468 /* A request completed. */
469 PRTFILEAIOREQINTERNAL pReqInt = OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped);
470 AssertPtr(pReqInt);
471 Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
472
473 /* Mark the request as finished. */
474 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
475
476 pReqInt->cbTransfered = cbTransfered;
477 if (fSucceeded)
478 pReqInt->Rc = VINF_SUCCESS;
479 else
480 {
481 DWORD errCode = GetLastError();
482 pReqInt->Rc = RTErrConvertFromWin32(errCode);
483 if (pReqInt->Rc == VERR_UNRESOLVED_ERROR)
484 LogRel(("AIO/win: Request %#p returned rc=%Rrc (native %u\n)", pReqInt, pReqInt->Rc, errCode));
485 }
486
487 pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt;
488
489 /* Update counter. */
490 cMinReqs--;
491
492 if (cMillies != RT_INDEFINITE_WAIT)
493 {
494 /* Recalculate timeout. */
495 uint64_t NanoTS = RTTimeNanoTS();
496 uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
497 if (cMilliesElapsed < cMillies)
498 cMillies -= cMilliesElapsed;
499 else
500 cMillies = 0;
501 }
502 }
503
504 /*
505 * Update the context state and set the return value.
506 */
507 *pcReqs = cRequestsCompleted;
508 ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
509
510 /*
511 * Clear the wakeup flag and set rc.
512 */
513 bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, false);
514
515 if ( fWokenUp
516 && RT_SUCCESS(rc))
517 rc = VERR_INTERRUPTED;
518
519 return rc;
520}
521
522RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
523{
524 int rc = VINF_SUCCESS;
525 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
526 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
527
528 bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true);
529 bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting);
530
531 if ( !fWokenUp
532 && fWaiting)
533 {
534 BOOL fSucceeded = PostQueuedCompletionStatus(pCtxInt->hIoCompletionPort,
535 0, AIO_CONTEXT_WAKEUP_EVENT,
536 NULL);
537
538 if (!fSucceeded)
539 rc = RTErrConvertFromWin32(GetLastError());
540 }
541
542 return rc;
543}
544
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