VirtualBox

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

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

header (C) fixes

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