VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/reqqueue.cpp

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
Line 
1/* $Id: reqqueue.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Request Queue.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/req.h>
42#include "internal/iprt.h"
43
44#include <iprt/assert.h>
45#include <iprt/asm.h>
46#include <iprt/err.h>
47#include <iprt/string.h>
48#include <iprt/time.h>
49#include <iprt/semaphore.h>
50#include <iprt/thread.h>
51#include <iprt/log.h>
52#include <iprt/mem.h>
53
54#include "internal/req.h"
55#include "internal/magics.h"
56
57
58
59RTDECL(int) RTReqQueueCreate(RTREQQUEUE *phQueue)
60{
61 PRTREQQUEUEINT pQueue = (PRTREQQUEUEINT)RTMemAllocZ(sizeof(RTREQQUEUEINT));
62 if (!pQueue)
63 return VERR_NO_MEMORY;
64 int rc = RTSemEventCreate(&pQueue->EventSem);
65 if (RT_SUCCESS(rc))
66 {
67 pQueue->u32Magic = RTREQQUEUE_MAGIC;
68
69 *phQueue = pQueue;
70 return VINF_SUCCESS;
71 }
72
73 RTMemFree(pQueue);
74 return rc;
75}
76RT_EXPORT_SYMBOL(RTReqQueueCreate);
77
78
79RTDECL(int) RTReqQueueDestroy(RTREQQUEUE hQueue)
80{
81 /*
82 * Check input.
83 */
84 if (hQueue == NIL_RTREQQUEUE)
85 return VINF_SUCCESS;
86 PRTREQQUEUEINT pQueue = hQueue;
87 AssertPtrReturn(pQueue, VERR_INVALID_HANDLE);
88 AssertReturn(ASMAtomicCmpXchgU32(&pQueue->u32Magic, RTREQQUEUE_MAGIC_DEAD, RTREQQUEUE_MAGIC), VERR_INVALID_HANDLE);
89
90 RTSemEventDestroy(pQueue->EventSem);
91 pQueue->EventSem = NIL_RTSEMEVENT;
92
93 for (unsigned i = 0; i < RT_ELEMENTS(pQueue->apReqFree); i++)
94 {
95 PRTREQ pReq = (PRTREQ)ASMAtomicXchgPtr((void **)&pQueue->apReqFree[i], NULL);
96 while (pReq)
97 {
98 PRTREQ pNext = pReq->pNext;
99 rtReqFreeIt(pReq);
100 pReq = pNext;
101 }
102 }
103
104 RTMemFree(pQueue);
105 return VINF_SUCCESS;
106}
107RT_EXPORT_SYMBOL(RTReqQueueDestroy);
108
109
110RTDECL(int) RTReqQueueProcess(RTREQQUEUE hQueue, RTMSINTERVAL cMillies)
111{
112 LogFlow(("RTReqQueueProcess %x\n", hQueue));
113
114 /*
115 * Check input.
116 */
117 PRTREQQUEUEINT pQueue = hQueue;
118 AssertPtrReturn(pQueue, VERR_INVALID_HANDLE);
119 AssertReturn(pQueue->u32Magic == RTREQQUEUE_MAGIC, VERR_INVALID_HANDLE);
120
121 /*
122 * Process loop. Stop (break) after the first non-VINF_SUCCESS status code.
123 */
124 int rc = VINF_SUCCESS;
125 for (;;)
126 {
127 /*
128 * Get pending requests.
129 */
130 PRTREQ pReqs = ASMAtomicXchgPtrT(&pQueue->pAlreadyPendingReqs, NULL, PRTREQ);
131 if (RT_LIKELY(!pReqs))
132 {
133 pReqs = ASMAtomicXchgPtrT(&pQueue->pReqs, NULL, PRTREQ);
134 if (!pReqs)
135 {
136 /* We do not adjust cMillies (documented behavior). */
137 ASMAtomicWriteBool(&pQueue->fBusy, false); /* this aint 100% perfect, but it's good enough for now... */
138 rc = RTSemEventWait(pQueue->EventSem, cMillies);
139 if (rc != VINF_SUCCESS)
140 break;
141 continue;
142 }
143
144 ASMAtomicWriteBool(&pQueue->fBusy, true);
145
146 /*
147 * Reverse the list to process it in FIFO order.
148 */
149 PRTREQ pReq = pReqs;
150 if (pReq->pNext)
151 Log2(("RTReqQueueProcess: 2+ requests: %p %p %p\n", pReq, pReq->pNext, pReq->pNext->pNext));
152 pReqs = NULL;
153 while (pReq)
154 {
155 Assert(pReq->enmState == RTREQSTATE_QUEUED);
156 Assert(pReq->uOwner.hQueue == pQueue);
157 PRTREQ pCur = pReq;
158 pReq = pReq->pNext;
159 pCur->pNext = pReqs;
160 pReqs = pCur;
161 }
162
163 }
164 else
165 ASMAtomicWriteBool(&pQueue->fBusy, true);
166
167 /*
168 * Process the requests.
169 */
170 while (pReqs)
171 {
172 /* Unchain the first request and advance the list. */
173 PRTREQ pReq = pReqs;
174 pReqs = pReqs->pNext;
175 pReq->pNext = NULL;
176
177 /* Process the request. */
178 rc = rtReqProcessOne(pReq);
179 if (rc != VINF_SUCCESS)
180 {
181 /* Propagate the return code to caller. If more requests pending, queue them for later. */
182 if (pReqs)
183 {
184 pReqs = ASMAtomicXchgPtrT(&pQueue->pAlreadyPendingReqs, pReqs, PRTREQ);
185 Assert(!pReqs);
186 }
187 break;
188 }
189 }
190 if (rc != VINF_SUCCESS)
191 break;
192 }
193
194 LogFlow(("RTReqQueueProcess: returns %Rrc\n", rc));
195 return rc;
196}
197RT_EXPORT_SYMBOL(RTReqQueueProcess);
198
199
200RTDECL(int) RTReqQueueCall(RTREQQUEUE hQueue, PRTREQ *ppReq, RTMSINTERVAL cMillies, PFNRT pfnFunction, unsigned cArgs, ...)
201{
202 va_list va;
203 va_start(va, cArgs);
204 int rc = RTReqQueueCallV(hQueue, ppReq, cMillies, RTREQFLAGS_IPRT_STATUS, pfnFunction, cArgs, va);
205 va_end(va);
206 return rc;
207}
208RT_EXPORT_SYMBOL(RTReqQueueCall);
209
210
211RTDECL(int) RTReqQueueCallVoid(RTREQQUEUE hQueue, PRTREQ *ppReq, RTMSINTERVAL cMillies, PFNRT pfnFunction, unsigned cArgs, ...)
212{
213 va_list va;
214 va_start(va, cArgs);
215 int rc = RTReqQueueCallV(hQueue, ppReq, cMillies, RTREQFLAGS_VOID, pfnFunction, cArgs, va);
216 va_end(va);
217 return rc;
218}
219RT_EXPORT_SYMBOL(RTReqQueueCallVoid);
220
221
222RTDECL(int) RTReqQueueCallEx(RTREQQUEUE hQueue, PRTREQ *ppReq, RTMSINTERVAL cMillies, unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, ...)
223{
224 va_list va;
225 va_start(va, cArgs);
226 int rc = RTReqQueueCallV(hQueue, ppReq, cMillies, fFlags, pfnFunction, cArgs, va);
227 va_end(va);
228 return rc;
229}
230RT_EXPORT_SYMBOL(RTReqQueueCallEx);
231
232
233RTDECL(int) RTReqQueueCallV(RTREQQUEUE hQueue, PRTREQ *ppReq, RTMSINTERVAL cMillies, unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, va_list Args)
234{
235 LogFlow(("RTReqQueueCallV: cMillies=%d fFlags=%#x pfnFunction=%p cArgs=%d\n", cMillies, fFlags, pfnFunction, cArgs));
236
237 /*
238 * Check input.
239 */
240 PRTREQQUEUEINT pQueue = hQueue;
241 AssertPtrReturn(pQueue, VERR_INVALID_HANDLE);
242 AssertReturn(pQueue->u32Magic == RTREQQUEUE_MAGIC, VERR_INVALID_HANDLE);
243 AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
244 AssertReturn(!(fFlags & ~(RTREQFLAGS_RETURN_MASK | RTREQFLAGS_NO_WAIT)), VERR_INVALID_PARAMETER);
245
246 if (!(fFlags & RTREQFLAGS_NO_WAIT) || ppReq)
247 {
248 AssertPtrReturn(ppReq, VERR_INVALID_POINTER);
249 *ppReq = NIL_RTREQ;
250 }
251
252 PRTREQ pReq = NULL;
253 AssertMsgReturn(cArgs <= 9, ("cArgs=%u\n", cArgs), VERR_TOO_MUCH_DATA); /* @bugref{10725} */
254 AssertMsgReturn(cArgs * sizeof(uintptr_t) <= sizeof(pReq->u.Internal.aArgs), ("cArgs=%u\n", cArgs), VERR_TOO_MUCH_DATA);
255
256 /*
257 * Allocate request
258 */
259 int rc = RTReqQueueAlloc(pQueue, RTREQTYPE_INTERNAL, &pReq);
260 if (rc != VINF_SUCCESS)
261 return rc;
262
263 /*
264 * Initialize the request data.
265 */
266 pReq->fFlags = fFlags;
267 pReq->u.Internal.pfn = pfnFunction;
268 pReq->u.Internal.cArgs = cArgs;
269 for (unsigned iArg = 0; iArg < cArgs; iArg++)
270 pReq->u.Internal.aArgs[iArg] = va_arg(Args, uintptr_t);
271
272 /*
273 * Queue the request and return.
274 */
275 rc = RTReqSubmit(pReq, cMillies);
276 if ( rc != VINF_SUCCESS
277 && rc != VERR_TIMEOUT)
278 {
279 RTReqRelease(pReq);
280 pReq = NULL;
281 }
282 if (ppReq)
283 {
284 *ppReq = pReq;
285 LogFlow(("RTReqQueueCallV: returns %Rrc *ppReq=%p\n", rc, pReq));
286 }
287 else
288 {
289 RTReqRelease(pReq);
290 LogFlow(("RTReqQueueCallV: returns %Rrc\n", rc));
291 }
292 Assert(rc != VERR_INTERRUPTED);
293 return rc;
294}
295RT_EXPORT_SYMBOL(RTReqQueueCallV);
296
297
298RTDECL(bool) RTReqQueueIsBusy(RTREQQUEUE hQueue)
299{
300 PRTREQQUEUEINT pQueue = hQueue;
301 AssertPtrReturn(pQueue, false);
302
303 if (ASMAtomicReadBool(&pQueue->fBusy))
304 return true;
305 if (ASMAtomicReadPtrT(&pQueue->pReqs, PRTREQ) != NULL)
306 return true;
307 if (ASMAtomicReadBool(&pQueue->fBusy))
308 return true;
309 return false;
310}
311RT_EXPORT_SYMBOL(RTReqQueueIsBusy);
312
313
314/**
315 * Joins the list pList with whatever is linked up at *pHead.
316 */
317static void vmr3ReqJoinFreeSub(volatile PRTREQ *ppHead, PRTREQ pList)
318{
319 for (unsigned cIterations = 0;; cIterations++)
320 {
321 PRTREQ pHead = ASMAtomicXchgPtrT(ppHead, pList, PRTREQ);
322 if (!pHead)
323 return;
324 PRTREQ pTail = pHead;
325 while (pTail->pNext)
326 pTail = pTail->pNext;
327 pTail->pNext = pList;
328 if (ASMAtomicCmpXchgPtr(ppHead, pHead, pList))
329 return;
330 pTail->pNext = NULL;
331 if (ASMAtomicCmpXchgPtr(ppHead, pHead, NULL))
332 return;
333 pList = pHead;
334 Assert(cIterations != 32);
335 Assert(cIterations != 64);
336 }
337}
338
339
340/**
341 * Joins the list pList with whatever is linked up at *pHead.
342 */
343static void vmr3ReqJoinFree(PRTREQQUEUEINT pQueue, PRTREQ pList)
344{
345 /*
346 * Split the list if it's too long.
347 */
348 unsigned cReqs = 1;
349 PRTREQ pTail = pList;
350 while (pTail->pNext)
351 {
352 if (cReqs++ > 25)
353 {
354 const uint32_t i = pQueue->iReqFree;
355 vmr3ReqJoinFreeSub(&pQueue->apReqFree[(i + 2) % RT_ELEMENTS(pQueue->apReqFree)], pTail->pNext);
356
357 pTail->pNext = NULL;
358 vmr3ReqJoinFreeSub(&pQueue->apReqFree[(i + 2 + (i == pQueue->iReqFree)) % RT_ELEMENTS(pQueue->apReqFree)], pTail->pNext);
359 return;
360 }
361 pTail = pTail->pNext;
362 }
363 vmr3ReqJoinFreeSub(&pQueue->apReqFree[(pQueue->iReqFree + 2) % RT_ELEMENTS(pQueue->apReqFree)], pList);
364}
365
366
367RTDECL(int) RTReqQueueAlloc(RTREQQUEUE hQueue, RTREQTYPE enmType, PRTREQ *phReq)
368{
369 /*
370 * Validate input.
371 */
372 PRTREQQUEUEINT pQueue = hQueue;
373 AssertPtrReturn(pQueue, VERR_INVALID_HANDLE);
374 AssertReturn(pQueue->u32Magic == RTREQQUEUE_MAGIC, VERR_INVALID_HANDLE);
375 AssertMsgReturn(enmType > RTREQTYPE_INVALID && enmType < RTREQTYPE_MAX, ("%d\n", enmType), VERR_RT_REQUEST_INVALID_TYPE);
376
377 /*
378 * Try get a recycled packet.
379 *
380 * While this could all be solved with a single list with a lock, it's a sport
381 * of mine to avoid locks.
382 */
383 int cTries = RT_ELEMENTS(pQueue->apReqFree) * 2;
384 while (--cTries >= 0)
385 {
386 PRTREQ volatile *ppHead = &pQueue->apReqFree[ASMAtomicIncU32(&pQueue->iReqFree) % RT_ELEMENTS(pQueue->apReqFree)];
387 PRTREQ pReq = ASMAtomicXchgPtrT(ppHead, NULL, PRTREQ);
388 if (pReq)
389 {
390 PRTREQ pNext = pReq->pNext;
391 if ( pNext
392 && !ASMAtomicCmpXchgPtr(ppHead, pNext, NULL))
393 vmr3ReqJoinFree(pQueue, pReq->pNext);
394 ASMAtomicDecU32(&pQueue->cReqFree);
395
396 Assert(pReq->uOwner.hQueue == pQueue);
397 Assert(!pReq->fPoolOrQueue);
398
399 int rc = rtReqReInit(pReq, enmType);
400 if (RT_SUCCESS(rc))
401 {
402 *phReq = pReq;
403 LogFlow(("RTReqQueueAlloc: returns VINF_SUCCESS *phReq=%p recycled\n", pReq));
404 return VINF_SUCCESS;
405 }
406 }
407 }
408
409 /*
410 * Ok, allocate a new one.
411 */
412 int rc = rtReqAlloc(enmType, false /*fPoolOrQueue*/, pQueue, phReq);
413 LogFlow(("RTReqQueueAlloc: returns %Rrc *phReq=%p\n", rc, *phReq));
414 return rc;
415}
416RT_EXPORT_SYMBOL(RTReqQueueAlloc);
417
418
419/**
420 * Recycles a requst.
421 *
422 * @returns true if recycled, false if it should be freed.
423 * @param pQueue The queue.
424 * @param pReq The request.
425 */
426DECLHIDDEN(bool) rtReqQueueRecycle(PRTREQQUEUEINT pQueue, PRTREQINT pReq)
427{
428 if ( !pQueue
429 || pQueue->cReqFree >= 128)
430 return false;
431
432 ASMAtomicIncU32(&pQueue->cReqFree);
433 PRTREQ volatile *ppHead = &pQueue->apReqFree[ASMAtomicIncU32(&pQueue->iReqFree) % RT_ELEMENTS(pQueue->apReqFree)];
434 PRTREQ pNext;
435 do
436 {
437 pNext = *ppHead;
438 ASMAtomicWritePtr(&pReq->pNext, pNext);
439 } while (!ASMAtomicCmpXchgPtr(ppHead, pReq, pNext));
440
441 return true;
442}
443
444
445/**
446 * Submits a request to the queue.
447 *
448 * @param pQueue The queue.
449 * @param pReq The request.
450 */
451DECLHIDDEN(void) rtReqQueueSubmit(PRTREQQUEUEINT pQueue, PRTREQINT pReq)
452{
453 PRTREQ pNext;
454 do
455 {
456 pNext = pQueue->pReqs;
457 pReq->pNext = pNext;
458 ASMAtomicWriteBool(&pQueue->fBusy, true);
459 } while (!ASMAtomicCmpXchgPtr(&pQueue->pReqs, pReq, pNext));
460
461 /*
462 * Notify queue thread.
463 */
464 RTSemEventSignal(pQueue->EventSem);
465}
466
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