VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use