VirtualBox

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

Last change on this file since 62584 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

  • 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 62477 2016-07-22 18:27:37Z vboxsync $ */
2/** @file
3 * IPRT - File async I/O, native implementation for the Windows host platform.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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 <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_OFFSETOF(RTFILEAIOREQINTERNAL, Overlapped)) )
125
126RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
127{
128 int rcBSD = 0;
129 AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
130
131 /* No limits known. */
132 pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS;
133 pAioLimits->cbBufferAlignment = 0;
134
135 return VINF_SUCCESS;
136}
137
138RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
139{
140 AssertPtrReturn(phReq, VERR_INVALID_POINTER);
141
142 PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL));
143 if (RT_UNLIKELY(!pReqInt))
144 return VERR_NO_MEMORY;
145
146 pReqInt->pCtxInt = NULL;
147 pReqInt->fCompleted = false;
148 pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
149 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
150
151 *phReq = (RTFILEAIOREQ)pReqInt;
152
153 return VINF_SUCCESS;
154}
155
156RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
157{
158 /*
159 * Validate the handle and ignore nil.
160 */
161 if (hReq == NIL_RTFILEAIOREQ)
162 return VINF_SUCCESS;
163 PRTFILEAIOREQINTERNAL pReqInt = hReq;
164 RTFILEAIOREQ_VALID_RETURN(pReqInt);
165 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
166
167 /*
168 * Trash the magic and free it.
169 */
170 ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
171 RTMemFree(pReqInt);
172 return VINF_SUCCESS;
173}
174
175/**
176 * Worker setting up the request.
177 */
178DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
179 TRANSFERDIRECTION enmTransferDirection,
180 RTFOFF off, void *pvBuf, size_t cbTransfer,
181 void *pvUser)
182{
183 /*
184 * Validate the input.
185 */
186 PRTFILEAIOREQINTERNAL pReqInt = hReq;
187 RTFILEAIOREQ_VALID_RETURN(pReqInt);
188 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
189 Assert(hFile != NIL_RTFILE);
190 AssertPtr(pvBuf);
191 Assert(off >= 0);
192 Assert(cbTransfer > 0);
193
194 pReqInt->enmTransferDirection = enmTransferDirection;
195 pReqInt->hFile = (HANDLE)RTFileToNative(hFile);
196 pReqInt->Overlapped.Offset = (DWORD)(off & 0xffffffff);
197 pReqInt->Overlapped.OffsetHigh = (DWORD)(off >> 32);
198 pReqInt->cbTransfer = cbTransfer;
199 pReqInt->pvBuf = pvBuf;
200 pReqInt->pvUser = pvUser;
201 pReqInt->fCompleted = false;
202
203 return VINF_SUCCESS;
204}
205
206RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
207 void *pvBuf, size_t cbRead, void *pvUser)
208{
209 return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_READ,
210 off, pvBuf, cbRead, pvUser);
211}
212
213RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
214 void const *pvBuf, size_t cbWrite, void *pvUser)
215{
216 return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_WRITE,
217 off, (void *)pvBuf, cbWrite, pvUser);
218}
219
220RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
221{
222 PRTFILEAIOREQINTERNAL pReqInt = hReq;
223 RTFILEAIOREQ_VALID_RETURN(pReqInt);
224 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
225 AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_HANDLE);
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,
274 uint32_t fFlags)
275{
276 PRTFILEAIOCTXINTERNAL pCtxInt;
277 AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
278 AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
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 return RTFILEAIO_UNLIMITED_REQS;
337}
338
339RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
340{
341 /*
342 * Parameter validation.
343 */
344 int rc = VINF_SUCCESS;
345 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
346 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
347 AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
348 Assert(cReqs <= INT32_MAX);
349 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
350 size_t i;
351
352 for (i = 0; i < cReqs; i++)
353 {
354 PRTFILEAIOREQINTERNAL pReqInt = pahReqs[i];
355 BOOL fSucceeded;
356
357 Assert(pReqInt->cbTransfer == (DWORD)pReqInt->cbTransfer);
358 if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_READ)
359 {
360 fSucceeded = ReadFile(pReqInt->hFile, pReqInt->pvBuf,
361 (DWORD)pReqInt->cbTransfer, NULL,
362 &pReqInt->Overlapped);
363 }
364 else if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_WRITE)
365 {
366 fSucceeded = WriteFile(pReqInt->hFile, pReqInt->pvBuf,
367 (DWORD)pReqInt->cbTransfer, NULL,
368 &pReqInt->Overlapped);
369 }
370 else
371 {
372 fSucceeded = false;
373 AssertMsgFailed(("Invalid transfer direction\n"));
374 }
375
376 if (RT_UNLIKELY(!fSucceeded && GetLastError() != ERROR_IO_PENDING))
377 {
378 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
379 rc = RTErrConvertFromWin32(GetLastError());
380 pReqInt->Rc = rc;
381 break;
382 }
383 RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
384 }
385
386 ASMAtomicAddS32(&pCtxInt->cRequests, (int32_t)i);
387
388 return rc;
389}
390
391RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
392 PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
393{
394 /*
395 * Validate the parameters, making sure to always set pcReqs.
396 */
397 AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
398 *pcReqs = 0; /* always set */
399 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
400 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
401 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
402 AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
403 AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
404
405 /*
406 * Can't wait if there are no requests around.
407 */
408 if ( RT_UNLIKELY(ASMAtomicUoReadS32(&pCtxInt->cRequests) == 0)
409 && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS))
410 return VERR_FILE_AIO_NO_REQUEST;
411
412 /* Wait for at least one. */
413 if (!cMinReqs)
414 cMinReqs = 1;
415
416 /*
417 * Loop until we're woken up, hit an error (incl timeout), or
418 * have collected the desired number of requests.
419 */
420 int rc = VINF_SUCCESS;
421 int cRequestsCompleted = 0;
422 while ( !pCtxInt->fWokenUp
423 && cMinReqs > 0)
424 {
425 uint64_t StartNanoTS = 0;
426 DWORD dwTimeout = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies;
427 DWORD cbTransfered;
428 LPOVERLAPPED pOverlapped;
429 ULONG_PTR lCompletionKey;
430 BOOL fSucceeded;
431
432 if (cMillies != RT_INDEFINITE_WAIT)
433 StartNanoTS = RTTimeNanoTS();
434
435 ASMAtomicXchgBool(&pCtxInt->fWaiting, true);
436 fSucceeded = GetQueuedCompletionStatus(pCtxInt->hIoCompletionPort,
437 &cbTransfered,
438 &lCompletionKey,
439 &pOverlapped,
440 dwTimeout);
441 ASMAtomicXchgBool(&pCtxInt->fWaiting, false);
442 if ( !fSucceeded
443 && !pOverlapped)
444 {
445 /* The call failed to dequeue a completion packet, includes VERR_TIMEOUT */
446 rc = RTErrConvertFromWin32(GetLastError());
447 break;
448 }
449
450 /* Check if we got woken up. */
451 if (lCompletionKey == AIO_CONTEXT_WAKEUP_EVENT)
452 {
453 Assert(fSucceeded && !pOverlapped);
454 break;
455 }
456
457 /* A request completed. */
458 PRTFILEAIOREQINTERNAL pReqInt = OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped);
459 AssertPtr(pReqInt);
460 Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
461
462 /* Mark the request as finished. */
463 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
464
465 pReqInt->cbTransfered = cbTransfered;
466 if (fSucceeded)
467 pReqInt->Rc = VINF_SUCCESS;
468 else
469 {
470 DWORD errCode = GetLastError();
471 pReqInt->Rc = RTErrConvertFromWin32(errCode);
472 if (pReqInt->Rc == VERR_UNRESOLVED_ERROR)
473 LogRel(("AIO/win: Request %#p returned rc=%Rrc (native %u\n)", pReqInt, pReqInt->Rc, errCode));
474 }
475
476 pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt;
477
478 /* Update counter. */
479 cMinReqs--;
480
481 if (cMillies != RT_INDEFINITE_WAIT)
482 {
483 /* Recalculate timeout. */
484 uint64_t NanoTS = RTTimeNanoTS();
485 uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
486 if (cMilliesElapsed < cMillies)
487 cMillies -= cMilliesElapsed;
488 else
489 cMillies = 0;
490 }
491 }
492
493 /*
494 * Update the context state and set the return value.
495 */
496 *pcReqs = cRequestsCompleted;
497 ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
498
499 /*
500 * Clear the wakeup flag and set rc.
501 */
502 bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, false);
503
504 if ( fWokenUp
505 && RT_SUCCESS(rc))
506 rc = VERR_INTERRUPTED;
507
508 return rc;
509}
510
511RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
512{
513 int rc = VINF_SUCCESS;
514 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
515 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
516
517 bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true);
518 bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting);
519
520 if ( !fWokenUp
521 && fWaiting)
522 {
523 BOOL fSucceeded = PostQueuedCompletionStatus(pCtxInt->hIoCompletionPort,
524 0, AIO_CONTEXT_WAKEUP_EVENT,
525 NULL);
526
527 if (!fSucceeded)
528 rc = RTErrConvertFromWin32(GetLastError());
529 }
530
531 return rc;
532}
533
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