VirtualBox

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

Last change on this file since 26788 was 26530, checked in by vboxsync, 15 years ago

fileaio-win.cpp: More warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/* $Id: fileaio-win.cpp 26530 2010-02-15 04:05:15Z vboxsync $ */
2/** @file
3 * IPRT - File async I/O, native implementation for the Windows host platform.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#define LOG_GROUP RTLOGGROUP_DIR
36
37#include <iprt/asm.h>
38#include <iprt/file.h>
39#include <iprt/mem.h>
40#include <iprt/assert.h>
41#include <iprt/string.h>
42#include <iprt/err.h>
43#include <iprt/log.h>
44#include "internal/fileaio.h"
45
46#include <Windows.h>
47
48/*******************************************************************************
49* Structures and Typedefs *
50*******************************************************************************/
51
52/**
53 * Transfer direction.
54 */
55typedef enum TRANSFERDIRECTION
56{
57 TRANSFERDIRECTION_INVALID = 0,
58 /** Read. */
59 TRANSFERDIRECTION_READ,
60 /** Write. */
61 TRANSFERDIRECTION_WRITE,
62 /** The usual 32-bit hack. */
63 TRANSFERDIRECTION_32BIT_HACK = 0x7fffffff
64} TRANSFERDIRECTION;
65
66/**
67 * Async I/O completion context state.
68 */
69typedef struct RTFILEAIOCTXINTERNAL
70{
71 /** handle to I/O completion port. */
72 HANDLE hIoCompletionPort;
73 /** Current number of requests pending. */
74 volatile int32_t cRequests;
75 /** Flag whether the thread was woken up. */
76 volatile bool fWokenUp;
77 /** Flag whether the thread is currently waiting. */
78 volatile bool fWaiting;
79 /** Magic value (RTFILEAIOCTX_MAGIC). */
80 uint32_t u32Magic;
81} RTFILEAIOCTXINTERNAL;
82/** Pointer to an internal context structure. */
83typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL;
84
85/**
86 * Async I/O request state.
87 */
88typedef struct RTFILEAIOREQINTERNAL
89{
90 /** Overlapped structure. */
91 OVERLAPPED Overlapped;
92 /** Current state the request is in. */
93 RTFILEAIOREQSTATE enmState;
94 /** The file handle. */
95 HANDLE hFile;
96 /** Kind of transfer Read/Write. */
97 TRANSFERDIRECTION enmTransferDirection;
98 /** Number of bytes to transfer. */
99 size_t cbTransfer;
100 /** Pointer to the buffer. */
101 void *pvBuf;
102 /** Opaque user data. */
103 void *pvUser;
104 /** Flag whether the request completed. */
105 bool fCompleted;
106 /** Number of bytes transfered successfully. */
107 size_t cbTransfered;
108 /** Error code of the completed request. */
109 int Rc;
110 /** Completion context we are assigned to. */
111 PRTFILEAIOCTXINTERNAL pCtxInt;
112 /** Magic value (RTFILEAIOREQ_MAGIC). */
113 uint32_t u32Magic;
114} RTFILEAIOREQINTERNAL;
115/** Pointer to an internal request structure. */
116typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL;
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)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 /** @todo: Flushing is not available */
228#if 0
229 return rtFileAsyncPrepareTransfer(pRequest, File, TRANSFERDIRECTION_FLUSH,
230 0, NULL, 0, pvUser);
231#endif
232 return VERR_NOT_IMPLEMENTED;
233}
234
235RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
236{
237 PRTFILEAIOREQINTERNAL pReqInt = hReq;
238 RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
239
240 return pReqInt->pvUser;
241}
242
243RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
244{
245 PRTFILEAIOREQINTERNAL pReqInt = hReq;
246 RTFILEAIOREQ_VALID_RETURN(pReqInt);
247 RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
248
249 /**
250 * @todo r=aeichner It is not possible to cancel specific
251 * requests on Windows before Vista.
252 * CancelIo cancels all requests for a file issued by the
253 * calling thread and CancelIoEx which does what we need
254 * is only available from Vista and up.
255 * The solution is to return VERR_FILE_AIO_IN_PROGRESS
256 * if the request didn't completed yet (checked above).
257 * Shouldn't be a big issue because a request is normally
258 * only canceled if it exceeds a timeout which is quite huge.
259 */
260 return VERR_FILE_AIO_COMPLETED;
261}
262
263RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
264{
265 int rc = VINF_SUCCESS;
266 PRTFILEAIOREQINTERNAL pReqInt = hReq;
267 RTFILEAIOREQ_VALID_RETURN(pReqInt);
268 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
269 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
270
271 rc = pReqInt->Rc;
272 if (pcbTransfered && RT_SUCCESS(rc))
273 *pcbTransfered = pReqInt->cbTransfered;
274
275 return rc;
276}
277
278RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax)
279{
280 PRTFILEAIOCTXINTERNAL pCtxInt;
281 AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
282
283 pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL));
284 if (RT_UNLIKELY(!pCtxInt))
285 return VERR_NO_MEMORY;
286
287 pCtxInt->hIoCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
288 NULL,
289 0,
290 0);
291 if (RT_UNLIKELY(!pCtxInt->hIoCompletionPort))
292 {
293 RTMemFree(pCtxInt);
294 return VERR_NO_MEMORY;
295 }
296
297 pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
298
299 *phAioCtx = (RTFILEAIOCTX)pCtxInt;
300
301 return VINF_SUCCESS;
302}
303
304RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
305{
306 /* Validate the handle and ignore nil. */
307 if (hAioCtx == NIL_RTFILEAIOCTX)
308 return VINF_SUCCESS;
309 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
310 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
311
312 /* Cannot destroy a busy context. */
313 if (RT_UNLIKELY(pCtxInt->cRequests))
314 return VERR_FILE_AIO_BUSY;
315
316 CloseHandle(pCtxInt->hIoCompletionPort);
317 ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD);
318 RTMemFree(pCtxInt);
319
320 return VINF_SUCCESS;
321}
322
323RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
324{
325 int rc = VINF_SUCCESS;
326 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
327 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
328
329 HANDLE hTemp = CreateIoCompletionPort((HANDLE)hFile, pCtxInt->hIoCompletionPort, 0, 1);
330 if (hTemp != pCtxInt->hIoCompletionPort)
331 rc = RTErrConvertFromWin32(GetLastError());
332
333 return rc;
334}
335
336RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
337{
338 return RTFILEAIO_UNLIMITED_REQS;
339}
340
341RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
342{
343 /*
344 * Parameter validation.
345 */
346 int rc = VINF_SUCCESS;
347 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
348 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
349 AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
350 Assert(cReqs <= INT32_MAX);
351 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
352 size_t i;
353
354 for (i = 0; i < cReqs; i++)
355 {
356 PRTFILEAIOREQINTERNAL pReqInt = pahReqs[i];
357 BOOL fSucceeded;
358
359 Assert(pReqInt->cbTransfer == (DWORD)pReqInt->cbTransfer);
360 if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_READ)
361 {
362 fSucceeded = ReadFile(pReqInt->hFile, pReqInt->pvBuf,
363 (DWORD)pReqInt->cbTransfer, NULL,
364 &pReqInt->Overlapped);
365 }
366 else if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_WRITE)
367 {
368 fSucceeded = WriteFile(pReqInt->hFile, pReqInt->pvBuf,
369 (DWORD)pReqInt->cbTransfer, NULL,
370 &pReqInt->Overlapped);
371 }
372 else
373 AssertMsgFailed(("Invalid transfer direction\n"));
374
375 if (RT_UNLIKELY(!fSucceeded && GetLastError() != ERROR_IO_PENDING))
376 {
377 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
378 rc = RTErrConvertFromWin32(GetLastError());
379 pReqInt->Rc = rc;
380 break;
381 }
382 RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
383 }
384
385 ASMAtomicAddS32(&pCtxInt->cRequests, (int32_t)i);
386
387 return rc;
388}
389
390RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
391 PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
392{
393 /*
394 * Validate the parameters, making sure to always set pcReqs.
395 */
396 AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
397 *pcReqs = 0; /* always set */
398 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
399 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
400 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
401 AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
402 AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
403
404 /*
405 * Can't wait if there are no requests around.
406 */
407 if (RT_UNLIKELY(ASMAtomicUoReadS32(&pCtxInt->cRequests) == 0))
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 {
442 /* Includes VERR_TIMEOUT */
443 rc = RTErrConvertFromWin32(GetLastError());
444 break;
445 }
446
447 /* Check if we got woken up. */
448 if (lCompletionKey == AIO_CONTEXT_WAKEUP_EVENT)
449 break;
450
451 /* A request completed. */
452 PRTFILEAIOREQINTERNAL pReqInt = OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped);
453 AssertPtr(pReqInt);
454 Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
455
456 /* Mark the request as finished. */
457 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
458
459 /* completion status. */
460 fSucceeded = GetOverlappedResult(pReqInt->hFile,
461 &pReqInt->Overlapped,
462 &cbTransfered,
463 FALSE);
464 pReqInt->cbTransfered = cbTransfered;
465 pReqInt->Rc = VINF_SUCCESS;
466
467 pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt;
468
469 /* Update counter. */
470 cMinReqs--;
471
472 if (cMillies != RT_INDEFINITE_WAIT)
473 {
474 /* Recalculate timeout. */
475 uint64_t NanoTS = RTTimeNanoTS();
476 uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
477 if (cMilliesElapsed < cMillies)
478 cMillies -= cMilliesElapsed;
479 else
480 cMillies = 0;
481 }
482 }
483
484 /*
485 * Update the context state and set the return value.
486 */
487 *pcReqs = cRequestsCompleted;
488 ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
489
490 /*
491 * Clear the wakeup flag and set rc.
492 */
493 bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, false);
494
495 if ( fWokenUp
496 && RT_SUCCESS(rc))
497 rc = VERR_INTERRUPTED;
498
499 return rc;
500}
501
502RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
503{
504 int rc = VINF_SUCCESS;
505 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
506 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
507
508 bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true);
509 bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting);
510
511 if ( !fWokenUp
512 && fWaiting)
513 {
514 BOOL fSucceeded = PostQueuedCompletionStatus(pCtxInt->hIoCompletionPort,
515 0, AIO_CONTEXT_WAKEUP_EVENT,
516 NULL);
517
518 if (!fSucceeded)
519 rc = RTErrConvertFromWin32(GetLastError());
520 }
521
522 return rc;
523}
524
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