VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 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.5 KB
Line 
1/* $Id: reqqueue.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Request Queue.
4 */
5
6/*
7 * Copyright (C) 2006-2023 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 * sizeof(uintptr_t) <= sizeof(pReq->u.Internal.aArgs), ("cArgs=%u\n", cArgs), VERR_TOO_MUCH_DATA);
254
255 /*
256 * Allocate request
257 */
258 int rc = RTReqQueueAlloc(pQueue, RTREQTYPE_INTERNAL, &pReq);
259 if (rc != VINF_SUCCESS)
260 return rc;
261
262 /*
263 * Initialize the request data.
264 */
265 pReq->fFlags = fFlags;
266 pReq->u.Internal.pfn = pfnFunction;
267 pReq->u.Internal.cArgs = cArgs;
268 for (unsigned iArg = 0; iArg < cArgs; iArg++)
269 pReq->u.Internal.aArgs[iArg] = va_arg(Args, uintptr_t);
270
271 /*
272 * Queue the request and return.
273 */
274 rc = RTReqSubmit(pReq, cMillies);
275 if ( rc != VINF_SUCCESS
276 && rc != VERR_TIMEOUT)
277 {
278 RTReqRelease(pReq);
279 pReq = NULL;
280 }
281 if (ppReq)
282 {
283 *ppReq = pReq;
284 LogFlow(("RTReqQueueCallV: returns %Rrc *ppReq=%p\n", rc, pReq));
285 }
286 else
287 {
288 RTReqRelease(pReq);
289 LogFlow(("RTReqQueueCallV: returns %Rrc\n", rc));
290 }
291 Assert(rc != VERR_INTERRUPTED);
292 return rc;
293}
294RT_EXPORT_SYMBOL(RTReqQueueCallV);
295
296
297RTDECL(bool) RTReqQueueIsBusy(RTREQQUEUE hQueue)
298{
299 PRTREQQUEUEINT pQueue = hQueue;
300 AssertPtrReturn(pQueue, false);
301
302 if (ASMAtomicReadBool(&pQueue->fBusy))
303 return true;
304 if (ASMAtomicReadPtrT(&pQueue->pReqs, PRTREQ) != NULL)
305 return true;
306 if (ASMAtomicReadBool(&pQueue->fBusy))
307 return true;
308 return false;
309}
310RT_EXPORT_SYMBOL(RTReqQueueIsBusy);
311
312
313/**
314 * Joins the list pList with whatever is linked up at *pHead.
315 */
316static void vmr3ReqJoinFreeSub(volatile PRTREQ *ppHead, PRTREQ pList)
317{
318 for (unsigned cIterations = 0;; cIterations++)
319 {
320 PRTREQ pHead = ASMAtomicXchgPtrT(ppHead, pList, PRTREQ);
321 if (!pHead)
322 return;
323 PRTREQ pTail = pHead;
324 while (pTail->pNext)
325 pTail = pTail->pNext;
326 pTail->pNext = pList;
327 if (ASMAtomicCmpXchgPtr(ppHead, pHead, pList))
328 return;
329 pTail->pNext = NULL;
330 if (ASMAtomicCmpXchgPtr(ppHead, pHead, NULL))
331 return;
332 pList = pHead;
333 Assert(cIterations != 32);
334 Assert(cIterations != 64);
335 }
336}
337
338
339/**
340 * Joins the list pList with whatever is linked up at *pHead.
341 */
342static void vmr3ReqJoinFree(PRTREQQUEUEINT pQueue, PRTREQ pList)
343{
344 /*
345 * Split the list if it's too long.
346 */
347 unsigned cReqs = 1;
348 PRTREQ pTail = pList;
349 while (pTail->pNext)
350 {
351 if (cReqs++ > 25)
352 {
353 const uint32_t i = pQueue->iReqFree;
354 vmr3ReqJoinFreeSub(&pQueue->apReqFree[(i + 2) % RT_ELEMENTS(pQueue->apReqFree)], pTail->pNext);
355
356 pTail->pNext = NULL;
357 vmr3ReqJoinFreeSub(&pQueue->apReqFree[(i + 2 + (i == pQueue->iReqFree)) % RT_ELEMENTS(pQueue->apReqFree)], pTail->pNext);
358 return;
359 }
360 pTail = pTail->pNext;
361 }
362 vmr3ReqJoinFreeSub(&pQueue->apReqFree[(pQueue->iReqFree + 2) % RT_ELEMENTS(pQueue->apReqFree)], pList);
363}
364
365
366RTDECL(int) RTReqQueueAlloc(RTREQQUEUE hQueue, RTREQTYPE enmType, PRTREQ *phReq)
367{
368 /*
369 * Validate input.
370 */
371 PRTREQQUEUEINT pQueue = hQueue;
372 AssertPtrReturn(pQueue, VERR_INVALID_HANDLE);
373 AssertReturn(pQueue->u32Magic == RTREQQUEUE_MAGIC, VERR_INVALID_HANDLE);
374 AssertMsgReturn(enmType > RTREQTYPE_INVALID && enmType < RTREQTYPE_MAX, ("%d\n", enmType), VERR_RT_REQUEST_INVALID_TYPE);
375
376 /*
377 * Try get a recycled packet.
378 *
379 * While this could all be solved with a single list with a lock, it's a sport
380 * of mine to avoid locks.
381 */
382 int cTries = RT_ELEMENTS(pQueue->apReqFree) * 2;
383 while (--cTries >= 0)
384 {
385 PRTREQ volatile *ppHead = &pQueue->apReqFree[ASMAtomicIncU32(&pQueue->iReqFree) % RT_ELEMENTS(pQueue->apReqFree)];
386 PRTREQ pReq = ASMAtomicXchgPtrT(ppHead, NULL, PRTREQ);
387 if (pReq)
388 {
389 PRTREQ pNext = pReq->pNext;
390 if ( pNext
391 && !ASMAtomicCmpXchgPtr(ppHead, pNext, NULL))
392 vmr3ReqJoinFree(pQueue, pReq->pNext);
393 ASMAtomicDecU32(&pQueue->cReqFree);
394
395 Assert(pReq->uOwner.hQueue == pQueue);
396 Assert(!pReq->fPoolOrQueue);
397
398 int rc = rtReqReInit(pReq, enmType);
399 if (RT_SUCCESS(rc))
400 {
401 *phReq = pReq;
402 LogFlow(("RTReqQueueAlloc: returns VINF_SUCCESS *phReq=%p recycled\n", pReq));
403 return VINF_SUCCESS;
404 }
405 }
406 }
407
408 /*
409 * Ok, allocate a new one.
410 */
411 int rc = rtReqAlloc(enmType, false /*fPoolOrQueue*/, pQueue, phReq);
412 LogFlow(("RTReqQueueAlloc: returns %Rrc *phReq=%p\n", rc, *phReq));
413 return rc;
414}
415RT_EXPORT_SYMBOL(RTReqQueueAlloc);
416
417
418/**
419 * Recycles a requst.
420 *
421 * @returns true if recycled, false if it should be freed.
422 * @param pQueue The queue.
423 * @param pReq The request.
424 */
425DECLHIDDEN(bool) rtReqQueueRecycle(PRTREQQUEUEINT pQueue, PRTREQINT pReq)
426{
427 if ( !pQueue
428 || pQueue->cReqFree >= 128)
429 return false;
430
431 ASMAtomicIncU32(&pQueue->cReqFree);
432 PRTREQ volatile *ppHead = &pQueue->apReqFree[ASMAtomicIncU32(&pQueue->iReqFree) % RT_ELEMENTS(pQueue->apReqFree)];
433 PRTREQ pNext;
434 do
435 {
436 pNext = *ppHead;
437 ASMAtomicWritePtr(&pReq->pNext, pNext);
438 } while (!ASMAtomicCmpXchgPtr(ppHead, pReq, pNext));
439
440 return true;
441}
442
443
444/**
445 * Submits a request to the queue.
446 *
447 * @param pQueue The queue.
448 * @param pReq The request.
449 */
450DECLHIDDEN(void) rtReqQueueSubmit(PRTREQQUEUEINT pQueue, PRTREQINT pReq)
451{
452 PRTREQ pNext;
453 do
454 {
455 pNext = pQueue->pReqs;
456 pReq->pNext = pNext;
457 ASMAtomicWriteBool(&pQueue->fBusy, true);
458 } while (!ASMAtomicCmpXchgPtr(&pQueue->pReqs, pReq, pNext));
459
460 /*
461 * Notify queue thread.
462 */
463 RTSemEventSignal(pQueue->EventSem);
464}
465
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use