VirtualBox

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

Last change on this file since 54985 was 54962, checked in by vboxsync, 10 years ago

On FreeBSD hosts use the correct semantics of the last parameter of sysctlbyname(). Thank you Jung-uk Kim.

  • 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 54962 2015-03-26 09:16:03Z 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 0); /* 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