VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/ioqueue/ioqueue-aiofile-provider.cpp@ 86127

Last change on this file since 86127 was 86127, checked in by vboxsync, 4 years ago

Runtime/common/ioqueue/ioqueue-aiofile-provider: Fix memory leak during destruction

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.4 KB
Line 
1/* $Id: ioqueue-aiofile-provider.cpp 86127 2020-09-15 11:21:56Z vboxsync $ */
2/** @file
3 * IPRT - I/O queue, Async I/O file provider.
4 */
5
6/*
7 * Copyright (C) 2019-2020 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_IOQUEUE
32#include <iprt/ioqueue.h>
33
34#include <iprt/asm.h>
35#include <iprt/errcore.h>
36#include <iprt/file.h>
37#include <iprt/log.h>
38#include <iprt/mem.h>
39#include <iprt/semaphore.h>
40#include <iprt/string.h>
41
42#include "internal/ioqueue.h"
43
44
45/*********************************************************************************************************************************
46* Structures and Typedefs *
47*********************************************************************************************************************************/
48
49
50/**
51 * Internal I/O queue provider instance data.
52 */
53typedef struct RTIOQUEUEPROVINT
54{
55 /** The async I/O context handle. */
56 RTFILEAIOCTX hAioCtx;
57 /** Pointer to the array of requests waiting for commit. */
58 PRTFILEAIOREQ pahReqsToCommit;
59 /** Maximum number of requests to wait for commit.. */
60 size_t cReqsToCommitMax;
61 /** Number of requests waiting for commit. */
62 uint32_t cReqsToCommit;
63 /** Array of free cached request handles. */
64 PRTFILEAIOREQ pahReqsFree;
65 /** Maximum number of cached requests. */
66 uint32_t cReqsFreeMax;
67 /** Number of free cached requests. */
68 volatile uint32_t cReqsFree;
69} RTIOQUEUEPROVINT;
70/** Pointer to the internal I/O queue provider instance data. */
71typedef RTIOQUEUEPROVINT *PRTIOQUEUEPROVINT;
72
73
74/*********************************************************************************************************************************
75* Internal Functions *
76*********************************************************************************************************************************/
77
78
79/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnIsSupported} */
80static DECLCALLBACK(bool) rtIoQueueAioFileProv_IsSupported(void)
81{
82 /* The common code/public API already checked for the proper handle type. */
83 /** @todo Check that the file was opened with async I/O enabled on some platforms? */
84 return true;
85}
86
87
88/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnQueueInit} */
89static DECLCALLBACK(int) rtIoQueueAioFileProv_QueueInit(RTIOQUEUEPROV hIoQueueProv, uint32_t fFlags,
90 uint32_t cSqEntries, uint32_t cCqEntries)
91{
92 RT_NOREF(fFlags, cCqEntries);
93
94 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
95 int rc = VINF_SUCCESS;
96
97 pThis->cReqsToCommitMax = cSqEntries;
98 pThis->cReqsFreeMax = cSqEntries;
99 pThis->cReqsFree = 0;
100
101 pThis->pahReqsToCommit = (PRTFILEAIOREQ)RTMemAllocZ(cSqEntries * sizeof(PRTFILEAIOREQ));
102 if (RT_LIKELY(pThis->pahReqsToCommit))
103 {
104 pThis->pahReqsFree = (PRTFILEAIOREQ)RTMemAllocZ(cSqEntries * sizeof(PRTFILEAIOREQ));
105 if (RT_LIKELY(pThis->pahReqsFree))
106 {
107 rc = RTFileAioCtxCreate(&pThis->hAioCtx, cSqEntries, RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS);
108 if (RT_SUCCESS(rc))
109 return VINF_SUCCESS;
110
111 RTMemFree(pThis->pahReqsFree);
112 }
113 else
114 rc = VERR_NO_MEMORY;
115
116 RTMemFree(pThis->pahReqsToCommit);
117 }
118 else
119 rc = VERR_NO_MEMORY;
120
121 return rc;
122}
123
124
125/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnQueueDestroy} */
126static DECLCALLBACK(void) rtIoQueueAioFileProv_QueueDestroy(RTIOQUEUEPROV hIoQueueProv)
127{
128 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
129
130 RTFileAioCtxDestroy(pThis->hAioCtx);
131
132 while (pThis->cReqsFree--)
133 {
134 RTFILEAIOREQ hReq = pThis->pahReqsFree[pThis->cReqsFree];
135 RTFileAioReqDestroy(hReq);
136 pThis->pahReqsFree[pThis->cReqsFree] = NULL;
137 }
138
139 RTMemFree(pThis->pahReqsFree);
140 RTMemFree(pThis->pahReqsToCommit);
141 RT_BZERO(pThis, sizeof(*pThis));
142}
143
144
145/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnHandleRegister} */
146static DECLCALLBACK(int) rtIoQueueAioFileProv_HandleRegister(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle)
147{
148 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
149
150 return RTFileAioCtxAssociateWithFile(pThis->hAioCtx, pHandle->u.hFile);
151}
152
153
154/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnHandleDeregister} */
155static DECLCALLBACK(int) rtIoQueueAioFileProv_HandleDeregister(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle)
156{
157 RT_NOREF(hIoQueueProv, pHandle);
158
159 /** @todo For Windows there doesn't seem to be a way to deregister the file handle without reopening the file,
160 *.for all other hosts this is a nop, just like the register method.
161 */
162 return VINF_SUCCESS;
163}
164
165
166/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnReqPrepare} */
167static DECLCALLBACK(int) rtIoQueueAioFileProv_ReqPrepare(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle, RTIOQUEUEOP enmOp,
168 uint64_t off, void *pvBuf, size_t cbBuf, uint32_t fReqFlags,
169 void *pvUser)
170{
171 RT_NOREF(fReqFlags);
172
173 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
174
175 /* Try to grab a free request structure from the cache. */
176 RTFILEAIOREQ hReq = NIL_RTFILEAIOREQ;
177 int rc = VINF_SUCCESS;
178 uint32_t cReqsFree = ASMAtomicReadU32(&pThis->cReqsFree);
179 if (cReqsFree)
180 {
181 do
182 {
183 cReqsFree = ASMAtomicReadU32(&pThis->cReqsFree);
184 hReq = pThis->pahReqsFree[pThis->cReqsFree - 1];
185 } while (!ASMAtomicCmpXchgU32(&pThis->cReqsFree, cReqsFree - 1, cReqsFree));
186 }
187 else
188 rc = RTFileAioReqCreate(&hReq);
189
190 if (RT_SUCCESS(rc))
191 {
192 switch (enmOp)
193 {
194 case RTIOQUEUEOP_READ:
195 rc = RTFileAioReqPrepareRead(hReq, pHandle->u.hFile, (RTFOFF)off, pvBuf, cbBuf, pvUser);
196 break;
197 case RTIOQUEUEOP_WRITE:
198 rc = RTFileAioReqPrepareWrite(hReq, pHandle->u.hFile, (RTFOFF)off, pvBuf, cbBuf, pvUser);
199 break;
200 case RTIOQUEUEOP_SYNC:
201 rc = RTFileAioReqPrepareFlush(hReq, pHandle->u.hFile, pvUser);
202 break;
203 default:
204 AssertMsgFailedReturn(("Invalid I/O queue operation: %d\n", enmOp), VERR_INTERNAL_ERROR);
205 }
206
207 if (RT_SUCCESS(rc))
208 pThis->pahReqsToCommit[pThis->cReqsToCommit++] = hReq;
209 else
210 {
211 int rc2 = RTFileAioReqDestroy(hReq);
212 Assert(rc2); RT_NOREF(rc2);
213 }
214 }
215
216 return rc;
217}
218
219
220/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnCommit} */
221static DECLCALLBACK(int) rtIoQueueAioFileProv_Commit(RTIOQUEUEPROV hIoQueueProv, uint32_t *pcReqsCommitted)
222{
223 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
224
225 int rc = RTFileAioCtxSubmit(pThis->hAioCtx, pThis->pahReqsToCommit, pThis->cReqsToCommit);
226 if (RT_SUCCESS(rc))
227 {
228 *pcReqsCommitted = pThis->cReqsToCommit;
229 pThis->cReqsToCommit = 0;
230 }
231
232 return rc;
233}
234
235
236/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnEvtWait} */
237static DECLCALLBACK(int) rtIoQueueAioFileProv_EvtWait(RTIOQUEUEPROV hIoQueueProv, PRTIOQUEUECEVT paCEvt, uint32_t cCEvt,
238 uint32_t cMinWait, uint32_t *pcCEvt, uint32_t fFlags)
239{
240 RT_NOREF(fFlags);
241
242 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
243 int rc = VINF_SUCCESS;
244 uint32_t idxCEvt = 0;
245
246 while ( RT_SUCCESS(rc)
247 && cMinWait
248 && cCEvt)
249 {
250 RTFILEAIOREQ ahReqs[64];
251 uint32_t cReqsCompleted = 0;
252
253 rc = RTFileAioCtxWait(pThis->hAioCtx, cMinWait, RT_INDEFINITE_WAIT,
254 &ahReqs[0], RT_MIN(RT_ELEMENTS(ahReqs), cCEvt), &cReqsCompleted);
255 if (RT_SUCCESS(rc))
256 {
257 for (unsigned i = 0; i < cReqsCompleted; i++)
258 {
259 RTFILEAIOREQ hReq = ahReqs[i];
260
261 paCEvt[idxCEvt].rcReq = RTFileAioReqGetRC(hReq, &paCEvt[idxCEvt].cbXfered);
262 paCEvt[idxCEvt].pvUser = RTFileAioReqGetUser(hReq);
263 idxCEvt++;
264
265 /* Try to insert the free request into the cache. */
266 uint32_t cReqsFree = ASMAtomicReadU32(&pThis->cReqsFree);
267 if (cReqsFree < pThis->cReqsFreeMax)
268 {
269 do
270 {
271 cReqsFree = ASMAtomicReadU32(&pThis->cReqsFree);
272 pThis->pahReqsFree[pThis->cReqsFree] = hReq;
273 } while (!ASMAtomicCmpXchgU32(&pThis->cReqsFree, cReqsFree + 1, cReqsFree));
274 }
275 else
276 rc = RTFileAioReqDestroy(hReq);
277 }
278
279 cCEvt -= cReqsCompleted;
280 cMinWait -= RT_MIN(cMinWait, cReqsCompleted);
281 }
282 }
283
284 *pcCEvt = idxCEvt;
285 return rc;
286}
287
288
289/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnEvtWaitWakeup} */
290static DECLCALLBACK(int) rtIoQueueAioFileProv_EvtWaitWakeup(RTIOQUEUEPROV hIoQueueProv)
291{
292 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
293
294 return RTFileAioCtxWakeup(pThis->hAioCtx);
295}
296
297
298/**
299 * Async file I/O queue provider virtual method table.
300 */
301RT_DECL_DATA_CONST(RTIOQUEUEPROVVTABLE const) g_RTIoQueueAioFileProv =
302{
303 /** uVersion */
304 RTIOQUEUEPROVVTABLE_VERSION,
305 /** pszId */
306 "AioFile",
307 /** cbIoQueueProv */
308 sizeof(RTIOQUEUEPROVINT),
309 /** enmHnd */
310 RTHANDLETYPE_FILE,
311 /** fFlags */
312 0,
313 /** pfnIsSupported */
314 rtIoQueueAioFileProv_IsSupported,
315 /** pfnQueueInit */
316 rtIoQueueAioFileProv_QueueInit,
317 /** pfnQueueDestroy */
318 rtIoQueueAioFileProv_QueueDestroy,
319 /** pfnHandleRegister */
320 rtIoQueueAioFileProv_HandleRegister,
321 /** pfnHandleDeregister */
322 rtIoQueueAioFileProv_HandleDeregister,
323 /** pfnReqPrepare */
324 rtIoQueueAioFileProv_ReqPrepare,
325 /** pfnReqPrepareSg */
326 NULL,
327 /** pfnCommit */
328 rtIoQueueAioFileProv_Commit,
329 /** pfnEvtWait */
330 rtIoQueueAioFileProv_EvtWait,
331 /** pfnEvtWaitWakeup */
332 rtIoQueueAioFileProv_EvtWaitWakeup,
333 /** uEndMarker */
334 RTIOQUEUEPROVVTABLE_VERSION
335};
336
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