VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/freebsd/fileaio-freebsd.cpp@ 45733

Last change on this file since 45733 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: 22.0 KB
Line 
1/* $Id: fileaio-freebsd.cpp 45678 2013-04-23 11:28:41Z vboxsync $ */
2/** @file
3 * IPRT - File async I/O, native implementation for the FreeBSD host platform.
4 */
5
6/*
7 * Copyright (C) 2006-2011 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_FILE
32#include <iprt/asm.h>
33#include <iprt/file.h>
34#include <iprt/mem.h>
35#include <iprt/assert.h>
36#include <iprt/string.h>
37#include <iprt/err.h>
38#include <iprt/log.h>
39#include <iprt/thread.h>
40#include "internal/fileaio.h"
41
42#include <sys/types.h>
43#include <sys/event.h>
44#include <sys/time.h>
45#include <sys/sysctl.h>
46#include <aio.h>
47#include <errno.h>
48#include <unistd.h>
49#include <fcntl.h>
50
51/*******************************************************************************
52* Structures and Typedefs *
53*******************************************************************************/
54/**
55 * Async I/O completion context state.
56 */
57typedef struct RTFILEAIOCTXINTERNAL
58{
59 /** Handle to the kernel queue. */
60 int iKQueue;
61 /** Current number of requests active on this context. */
62 volatile int32_t cRequests;
63 /** The ID of the thread which is currently waiting for requests. */
64 volatile RTTHREAD hThreadWait;
65 /** Flag whether the thread was woken up. */
66 volatile bool fWokenUp;
67 /** Flag whether the thread is currently waiting in the syscall. */
68 volatile bool fWaiting;
69 /** Flags given during creation. */
70 uint32_t fFlags;
71 /** Magic value (RTFILEAIOCTX_MAGIC). */
72 uint32_t u32Magic;
73} RTFILEAIOCTXINTERNAL;
74/** Pointer to an internal context structure. */
75typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL;
76
77/**
78 * Async I/O request state.
79 */
80typedef struct RTFILEAIOREQINTERNAL
81{
82 /** The aio control block. Must be the FIRST
83 * element. */
84 struct aiocb AioCB;
85 /** Current state the request is in. */
86 RTFILEAIOREQSTATE enmState;
87 /** Flag whether this is a flush request. */
88 bool fFlush;
89 /** Opaque user data. */
90 void *pvUser;
91 /** Completion context we are assigned to. */
92 PRTFILEAIOCTXINTERNAL pCtxInt;
93 /** Number of bytes actually transferred. */
94 size_t cbTransfered;
95 /** Status code. */
96 int Rc;
97 /** Magic value (RTFILEAIOREQ_MAGIC). */
98 uint32_t u32Magic;
99} RTFILEAIOREQINTERNAL;
100/** Pointer to an internal request structure. */
101typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL;
102
103
104/*******************************************************************************
105* Defined Constants And Macros *
106*******************************************************************************/
107/** The max number of events to get in one call. */
108#define AIO_MAXIMUM_REQUESTS_PER_CONTEXT 64
109
110RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
111{
112 int rcBSD = 0;
113 AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
114
115 /*
116 * The AIO API is implemented in a kernel module which is not
117 * loaded by default.
118 * If it is loaded there are additional sysctl parameters.
119 */
120 int cReqsOutstandingMax = 0;
121 size_t cbParameter = sizeof(int);
122
123 rcBSD = sysctlbyname("vfs.aio.max_aio_per_proc", /* name */
124 &cReqsOutstandingMax, /* Where to store the old value. */
125 &cbParameter, /* Size of the memory pointed to. */
126 NULL, /* Where the new value is located. */
127 NULL); /* Where the size of the new value is stored. */
128 if (rcBSD == -1)
129 {
130 /* ENOENT means the value is unknown thus the module is not loaded. */
131 if (errno == ENOENT)
132 return VERR_NOT_SUPPORTED;
133 else
134 return RTErrConvertFromErrno(errno);
135 }
136
137 pAioLimits->cReqsOutstandingMax = cReqsOutstandingMax;
138 pAioLimits->cbBufferAlignment = 0;
139
140 return VINF_SUCCESS;
141}
142
143RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
144{
145 AssertPtrReturn(phReq, VERR_INVALID_POINTER);
146
147 PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL));
148 if (RT_UNLIKELY(!pReqInt))
149 return VERR_NO_MEMORY;
150
151 /* Ininitialize static parts. */
152 pReqInt->AioCB.aio_sigevent.sigev_notify = SIGEV_KEVENT;
153 pReqInt->AioCB.aio_sigevent.sigev_value.sival_ptr = pReqInt;
154 pReqInt->pCtxInt = NULL;
155 pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
156 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
157
158 *phReq = (RTFILEAIOREQ)pReqInt;
159
160 return VINF_SUCCESS;
161}
162
163RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
164{
165 /*
166 * Validate the handle and ignore nil.
167 */
168 if (hReq == NIL_RTFILEAIOREQ)
169 return VINF_SUCCESS;
170 PRTFILEAIOREQINTERNAL pReqInt = hReq;
171 RTFILEAIOREQ_VALID_RETURN(pReqInt);
172 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
173
174 /*
175 * Trash the magic and free it.
176 */
177 ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
178 RTMemFree(pReqInt);
179 return VINF_SUCCESS;
180}
181
182/**
183 * Worker setting up the request.
184 */
185DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
186 unsigned uTransferDirection,
187 RTFOFF off, void *pvBuf, size_t cbTransfer,
188 void *pvUser)
189{
190 /*
191 * Validate the input.
192 */
193 PRTFILEAIOREQINTERNAL pReqInt = hReq;
194 RTFILEAIOREQ_VALID_RETURN(pReqInt);
195 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
196 Assert(hFile != NIL_RTFILE);
197 AssertPtr(pvBuf);
198 Assert(off >= 0);
199 Assert(cbTransfer > 0);
200
201 pReqInt->AioCB.aio_sigevent.sigev_notify = SIGEV_KEVENT;
202 pReqInt->AioCB.aio_sigevent.sigev_value.sival_ptr = pReqInt;
203 pReqInt->AioCB.aio_lio_opcode = uTransferDirection;
204 pReqInt->AioCB.aio_fildes = RTFileToNative(hFile);
205 pReqInt->AioCB.aio_offset = off;
206 pReqInt->AioCB.aio_nbytes = cbTransfer;
207 pReqInt->AioCB.aio_buf = pvBuf;
208 pReqInt->fFlush = false;
209 pReqInt->pvUser = pvUser;
210 pReqInt->pCtxInt = NULL;
211 pReqInt->Rc = VERR_FILE_AIO_IN_PROGRESS;
212 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
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, LIO_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, LIO_WRITE,
228 off, (void *)pvBuf, cbWrite, pvUser);
229}
230
231RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
232{
233 PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)hReq;
234
235 RTFILEAIOREQ_VALID_RETURN(pReqInt);
236 Assert(hFile != NIL_RTFILE);
237 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
238
239 pReqInt->fFlush = true;
240 pReqInt->AioCB.aio_fildes = RTFileToNative(hFile);
241 pReqInt->AioCB.aio_offset = 0;
242 pReqInt->AioCB.aio_nbytes = 0;
243 pReqInt->AioCB.aio_buf = NULL;
244 pReqInt->pvUser = pvUser;
245 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
246
247 return VINF_SUCCESS;
248}
249
250RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
251{
252 PRTFILEAIOREQINTERNAL pReqInt = hReq;
253 RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
254
255 return pReqInt->pvUser;
256}
257
258RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
259{
260 PRTFILEAIOREQINTERNAL pReqInt = hReq;
261 RTFILEAIOREQ_VALID_RETURN(pReqInt);
262 RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
263
264
265 int rcBSD = aio_cancel(pReqInt->AioCB.aio_fildes, &pReqInt->AioCB);
266
267 if (rcBSD == AIO_CANCELED)
268 {
269 /*
270 * Decrement request count because the request will never arrive at the
271 * completion port.
272 */
273 AssertMsg(VALID_PTR(pReqInt->pCtxInt),
274 ("Invalid state. Request was canceled but wasn't submitted\n"));
275
276 ASMAtomicDecS32(&pReqInt->pCtxInt->cRequests);
277 pReqInt->Rc = VERR_FILE_AIO_CANCELED;
278 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
279 return VINF_SUCCESS;
280 }
281 else if (rcBSD == AIO_ALLDONE)
282 return VERR_FILE_AIO_COMPLETED;
283 else if (rcBSD == AIO_NOTCANCELED)
284 return VERR_FILE_AIO_IN_PROGRESS;
285 else
286 return RTErrConvertFromErrno(errno);
287}
288
289RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
290{
291 PRTFILEAIOREQINTERNAL pReqInt = hReq;
292 RTFILEAIOREQ_VALID_RETURN(pReqInt);
293 AssertPtrNull(pcbTransfered);
294 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
295 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
296
297 if ( (RT_SUCCESS(pReqInt->Rc))
298 && (pcbTransfered))
299 *pcbTransfered = pReqInt->cbTransfered;
300
301 return pReqInt->Rc;
302}
303
304RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax,
305 uint32_t fFlags)
306{
307 int rc = VINF_SUCCESS;
308 PRTFILEAIOCTXINTERNAL pCtxInt;
309 AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
310 AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
311
312 pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL));
313 if (RT_UNLIKELY(!pCtxInt))
314 return VERR_NO_MEMORY;
315
316 /* Init the event handle. */
317 pCtxInt->iKQueue = kqueue();
318 if (RT_LIKELY(pCtxInt->iKQueue > 0))
319 {
320 pCtxInt->fFlags = fFlags;
321 pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
322 *phAioCtx = (RTFILEAIOCTX)pCtxInt;
323 }
324 else
325 {
326 RTMemFree(pCtxInt);
327 rc = RTErrConvertFromErrno(errno);
328 }
329
330 return rc;
331}
332
333RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
334{
335 /* Validate the handle and ignore nil. */
336 if (hAioCtx == NIL_RTFILEAIOCTX)
337 return VINF_SUCCESS;
338 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
339 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
340
341 /* Cannot destroy a busy context. */
342 if (RT_UNLIKELY(pCtxInt->cRequests))
343 return VERR_FILE_AIO_BUSY;
344
345 close(pCtxInt->iKQueue);
346 ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD);
347 RTMemFree(pCtxInt);
348
349 return VINF_SUCCESS;
350}
351
352RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
353{
354 return RTFILEAIO_UNLIMITED_REQS;
355}
356
357RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
358{
359 return VINF_SUCCESS;
360}
361
362RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
363{
364 /*
365 * Parameter validation.
366 */
367 int rc = VINF_SUCCESS;
368 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
369 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
370 AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
371 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
372
373 do
374 {
375 int rcBSD = 0;
376 size_t cReqsSubmit = 0;
377 size_t i = 0;
378 PRTFILEAIOREQINTERNAL pReqInt;
379
380 while ( (i < cReqs)
381 && (i < AIO_LISTIO_MAX))
382 {
383 pReqInt = pahReqs[i];
384 if (RTFILEAIOREQ_IS_NOT_VALID(pReqInt))
385 {
386 /* Undo everything and stop submitting. */
387 for (size_t iUndo = 0; iUndo < i; iUndo++)
388 {
389 pReqInt = pahReqs[iUndo];
390 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
391 pReqInt->pCtxInt = NULL;
392 pReqInt->AioCB.aio_sigevent.sigev_notify_kqueue = 0;
393 }
394 rc = VERR_INVALID_HANDLE;
395 break;
396 }
397
398 pReqInt->AioCB.aio_sigevent.sigev_notify_kqueue = pCtxInt->iKQueue;
399 pReqInt->pCtxInt = pCtxInt;
400 RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
401
402 if (pReqInt->fFlush)
403 break;
404
405 cReqsSubmit++;
406 i++;
407 }
408
409 if (cReqsSubmit)
410 {
411 rcBSD = lio_listio(LIO_NOWAIT, (struct aiocb **)pahReqs, cReqsSubmit, NULL);
412 if (RT_UNLIKELY(rcBSD < 0))
413 {
414 if (errno == EAGAIN)
415 rc = VERR_FILE_AIO_INSUFFICIENT_RESSOURCES;
416 else
417 rc = RTErrConvertFromErrno(errno);
418
419 /* Check which requests got actually submitted and which not. */
420 for (i = 0; i < cReqs; i++)
421 {
422 pReqInt = pahReqs[i];
423 rcBSD = aio_error(&pReqInt->AioCB);
424 if ( rcBSD == -1
425 && errno == EINVAL)
426 {
427 /* Was not submitted. */
428 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
429 pReqInt->pCtxInt = NULL;
430 }
431 else if (rcBSD != EINPROGRESS)
432 {
433 /* The request encountered an error. */
434 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
435 pReqInt->Rc = RTErrConvertFromErrno(rcBSD);
436 pReqInt->pCtxInt = NULL;
437 pReqInt->cbTransfered = 0;
438 }
439 }
440 break;
441 }
442
443 ASMAtomicAddS32(&pCtxInt->cRequests, cReqsSubmit);
444 cReqs -= cReqsSubmit;
445 pahReqs += cReqsSubmit;
446 }
447
448 /* Check if we have a flush request now. */
449 if (cReqs && RT_SUCCESS_NP(rc))
450 {
451 pReqInt = pahReqs[0];
452 RTFILEAIOREQ_VALID_RETURN(pReqInt);
453
454 if (pReqInt->fFlush)
455 {
456 /*
457 * lio_listio does not work with flush requests so
458 * we have to use aio_fsync directly.
459 */
460 rcBSD = aio_fsync(O_SYNC, &pReqInt->AioCB);
461 if (RT_UNLIKELY(rcBSD < 0))
462 {
463 if (rcBSD == EAGAIN)
464 {
465 /* Was not submitted. */
466 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
467 pReqInt->pCtxInt = NULL;
468 return VERR_FILE_AIO_INSUFFICIENT_RESSOURCES;
469 }
470 else
471 {
472 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
473 pReqInt->Rc = RTErrConvertFromErrno(errno);
474 pReqInt->cbTransfered = 0;
475 return pReqInt->Rc;
476 }
477 }
478
479 ASMAtomicIncS32(&pCtxInt->cRequests);
480 cReqs--;
481 pahReqs++;
482 }
483 }
484 } while (cReqs);
485
486 return rc;
487}
488
489RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
490 PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
491{
492 int rc = VINF_SUCCESS;
493 int cRequestsCompleted = 0;
494
495 /*
496 * Validate the parameters, making sure to always set pcReqs.
497 */
498 AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
499 *pcReqs = 0; /* always set */
500 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
501 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
502 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
503 AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
504 AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
505
506 if ( RT_UNLIKELY(ASMAtomicReadS32(&pCtxInt->cRequests) == 0)
507 && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS))
508 return VERR_FILE_AIO_NO_REQUEST;
509
510 /*
511 * Convert the timeout if specified.
512 */
513 struct timespec *pTimeout = NULL;
514 struct timespec Timeout = {0,0};
515 uint64_t StartNanoTS = 0;
516 if (cMillies != RT_INDEFINITE_WAIT)
517 {
518 Timeout.tv_sec = cMillies / 1000;
519 Timeout.tv_nsec = cMillies % 1000 * 1000000;
520 pTimeout = &Timeout;
521 StartNanoTS = RTTimeNanoTS();
522 }
523
524 /* Wait for at least one. */
525 if (!cMinReqs)
526 cMinReqs = 1;
527
528 /* For the wakeup call. */
529 Assert(pCtxInt->hThreadWait == NIL_RTTHREAD);
530 ASMAtomicWriteHandle(&pCtxInt->hThreadWait, RTThreadSelf());
531
532 while ( cMinReqs
533 && RT_SUCCESS_NP(rc))
534 {
535 struct kevent aKEvents[AIO_MAXIMUM_REQUESTS_PER_CONTEXT];
536 int cRequestsToWait = cMinReqs < AIO_MAXIMUM_REQUESTS_PER_CONTEXT ? cReqs : AIO_MAXIMUM_REQUESTS_PER_CONTEXT;
537 int rcBSD;
538 uint64_t StartTime;
539
540 ASMAtomicXchgBool(&pCtxInt->fWaiting, true);
541 rcBSD = kevent(pCtxInt->iKQueue, NULL, 0, aKEvents, cRequestsToWait, pTimeout);
542 ASMAtomicXchgBool(&pCtxInt->fWaiting, false);
543
544 if (RT_UNLIKELY(rcBSD < 0))
545 {
546 rc = RTErrConvertFromErrno(errno);
547 break;
548 }
549
550 uint32_t const cDone = rcBSD;
551
552 /* Process received events. */
553 for (uint32_t i = 0; i < cDone; i++)
554 {
555 PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)aKEvents[i].udata;
556 AssertPtr(pReqInt);
557 Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
558
559 /*
560 * Retrieve the status code here already because the
561 * user may omit the RTFileAioReqGetRC() call and
562 * we will leak kernel resources then.
563 * This will result in errors during submission
564 * of other requests as soon as the max_aio_queue_per_proc
565 * limit is reached.
566 */
567 int cbTransfered = aio_return(&pReqInt->AioCB);
568
569 if (cbTransfered < 0)
570 {
571 pReqInt->Rc = RTErrConvertFromErrno(cbTransfered);
572 pReqInt->cbTransfered = 0;
573 }
574 else
575 {
576 pReqInt->Rc = VINF_SUCCESS;
577 pReqInt->cbTransfered = cbTransfered;
578 }
579 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
580 pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt;
581 }
582
583 /*
584 * Done Yet? If not advance and try again.
585 */
586 if (cDone >= cMinReqs)
587 break;
588 cMinReqs -= cDone;
589 cReqs -= cDone;
590
591 if (cMillies != RT_INDEFINITE_WAIT)
592 {
593 /* The API doesn't return ETIMEDOUT, so we have to fix that ourselves. */
594 uint64_t NanoTS = RTTimeNanoTS();
595 uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
596 if (cMilliesElapsed >= cMillies)
597 {
598 rc = VERR_TIMEOUT;
599 break;
600 }
601
602 /* The syscall supposedly updates it, but we're paranoid. :-) */
603 Timeout.tv_sec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) / 1000;
604 Timeout.tv_nsec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) % 1000 * 1000000;
605 }
606 }
607
608 /*
609 * Update the context state and set the return value.
610 */
611 *pcReqs = cRequestsCompleted;
612 ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
613 Assert(pCtxInt->hThreadWait == RTThreadSelf());
614 ASMAtomicWriteHandle(&pCtxInt->hThreadWait, NIL_RTTHREAD);
615
616 /*
617 * Clear the wakeup flag and set rc.
618 */
619 if ( pCtxInt->fWokenUp
620 && RT_SUCCESS(rc))
621 {
622 ASMAtomicXchgBool(&pCtxInt->fWokenUp, false);
623 rc = VERR_INTERRUPTED;
624 }
625
626 return rc;
627}
628
629RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
630{
631 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
632 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
633
634 /** @todo r=bird: Define the protocol for how to resume work after calling
635 * this function. */
636
637 bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true);
638
639 /*
640 * Read the thread handle before the status flag.
641 * If we read the handle after the flag we might
642 * end up with an invalid handle because the thread
643 * waiting in RTFileAioCtxWakeup() might get scheduled
644 * before we read the flag and returns.
645 * We can ensure that the handle is valid if fWaiting is true
646 * when reading the handle before the status flag.
647 */
648 RTTHREAD hThread;
649 ASMAtomicReadHandle(&pCtxInt->hThreadWait, &hThread);
650 bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting);
651 if ( !fWokenUp
652 && fWaiting)
653 {
654 /*
655 * If a thread waits the handle must be valid.
656 * It is possible that the thread returns from
657 * kevent() before the signal is send.
658 * This is no problem because we already set fWokenUp
659 * to true which will let the thread return VERR_INTERRUPTED
660 * and the next call to RTFileAioCtxWait() will not
661 * return VERR_INTERRUPTED because signals are not saved
662 * and will simply vanish if the destination thread can't
663 * receive it.
664 */
665 Assert(hThread != NIL_RTTHREAD);
666 RTThreadPoke(hThread);
667 }
668
669 return VINF_SUCCESS;
670}
671
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