VirtualBox

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

Last change on this file since 95765 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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