VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use