VirtualBox

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

Last change on this file since 46727 was 45678, checked in by vboxsync, 12 years ago

Runtime/aio: Add flags parameter to RTFileAioCtxCreate

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