VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/req.cpp@ 34497

Last change on this file since 34497 was 33595, checked in by vboxsync, 14 years ago

src/*: more spelling fixes (logging), thanks Timeless!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 31.9 KB
Line 
1/* $Id: req.cpp 33595 2010-10-29 10:35:00Z vboxsync $ */
2/** @file
3 * IPRT - Request packets
4 */
5
6/*
7 * Copyright (C) 2006-2007 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#include <iprt/req.h>
32#include "internal/iprt.h"
33
34#include <iprt/assert.h>
35#include <iprt/asm.h>
36#include <iprt/string.h>
37#include <iprt/time.h>
38#include <iprt/semaphore.h>
39#include <iprt/thread.h>
40#include <iprt/log.h>
41#include <iprt/mem.h>
42
43
44/*******************************************************************************
45* Internal Functions *
46*******************************************************************************/
47static int rtReqProcessOne(PRTREQ pReq);
48
49
50
51/**
52 * Create a request packet queue
53 *
54 * @returns iprt status code.
55 * @param ppQueue Where to store the request queue pointer.
56 */
57RTDECL(int) RTReqCreateQueue(PRTREQQUEUE *ppQueue)
58{
59 PRTREQQUEUE pQueue = (PRTREQQUEUE)RTMemAllocZ(sizeof(RTREQQUEUE));
60 if (!pQueue)
61 return VERR_NO_MEMORY;
62 int rc = RTSemEventCreate(&pQueue->EventSem);
63 if (RT_SUCCESS(rc))
64 {
65 *ppQueue = pQueue;
66 return VINF_SUCCESS;
67 }
68
69 RTMemFree(pQueue);
70 return rc;
71}
72RT_EXPORT_SYMBOL(RTReqCreateQueue);
73
74
75/**
76 * Destroy a request packet queue
77 *
78 * @returns iprt status code.
79 * @param pQueue The request queue.
80 */
81RTDECL(int) RTReqDestroyQueue(PRTREQQUEUE pQueue)
82{
83 /*
84 * Check input.
85 */
86 if (!pQueue)
87 return VINF_SUCCESS;
88 AssertPtr(pQueue);
89 RTSemEventDestroy(pQueue->EventSem);
90 pQueue->EventSem = NIL_RTSEMEVENT;
91 RTMemFree(pQueue);
92 return VINF_SUCCESS;
93}
94RT_EXPORT_SYMBOL(RTReqDestroyQueue);
95
96
97/**
98 * Process one or more request packets
99 *
100 * @returns iprt status code.
101 * @returns VERR_TIMEOUT if cMillies was reached without the packet being added.
102 *
103 * @param pQueue The request queue.
104 * @param cMillies Number of milliseconds to wait for a pending request.
105 * Use RT_INDEFINITE_WAIT to only wait till one is added.
106 */
107RTDECL(int) RTReqProcess(PRTREQQUEUE pQueue, RTMSINTERVAL cMillies)
108{
109 LogFlow(("RTReqProcess %x\n", pQueue));
110 /*
111 * Check input.
112 */
113 if (!pQueue)
114 {
115 AssertFailed();
116 return VERR_INVALID_PARAMETER;
117 }
118
119 /*
120 * Process loop.
121 *
122 * We do not repeat the outer loop if we've got an informational status code
123 * since that code needs processing by our caller.
124 */
125 int rc = VINF_SUCCESS;
126 while (rc <= VINF_SUCCESS)
127 {
128 /*
129 * Get pending requests.
130 */
131 PRTREQ pReqs = ASMAtomicXchgPtrT(&pQueue->pReqs, NULL, PRTREQ);
132 if (!pReqs)
133 {
134 ASMAtomicWriteBool(&pQueue->fBusy, false); /* this aint 100% perfect, but it's good enough for now... */
135 /** @todo We currently don't care if the entire time wasted here is larger than
136 * cMillies */
137 rc = RTSemEventWait(pQueue->EventSem, cMillies);
138 if (rc != VINF_SUCCESS)
139 break;
140 continue;
141 }
142 ASMAtomicWriteBool(&pQueue->fBusy, true);
143
144 /*
145 * Reverse the list to process it in FIFO order.
146 */
147 PRTREQ pReq = pReqs;
148 if (pReq->pNext)
149 Log2(("RTReqProcess: 2+ requests: %p %p %p\n", pReq, pReq->pNext, pReq->pNext->pNext));
150 pReqs = NULL;
151 while (pReq)
152 {
153 Assert(pReq->enmState == RTREQSTATE_QUEUED);
154 Assert(pReq->pQueue == pQueue);
155 PRTREQ pCur = pReq;
156 pReq = pReq->pNext;
157 pCur->pNext = pReqs;
158 pReqs = pCur;
159 }
160
161
162 /*
163 * Process the requests.
164 */
165 while (pReqs)
166 {
167 /* Unchain the first request and advance the list. */
168 pReq = pReqs;
169 pReqs = pReqs->pNext;
170 pReq->pNext = NULL;
171
172 /* Process the request */
173 rc = rtReqProcessOne(pReq);
174 AssertRC(rc);
175 if (rc != VINF_SUCCESS)
176 break; /** @todo r=bird: we're dropping requests here! Add 2nd queue that can hold them. (will fix when writing a testcase) */
177 }
178 }
179
180 LogFlow(("RTReqProcess: returns %Rrc\n", rc));
181 return rc;
182}
183RT_EXPORT_SYMBOL(RTReqProcess);
184
185/**
186 * Allocate and queue a call request.
187 *
188 * If it's desired to poll on the completion of the request set cMillies
189 * to 0 and use RTReqWait() to check for completion. In the other case
190 * use RT_INDEFINITE_WAIT.
191 * The returned request packet must be freed using RTReqFree().
192 *
193 * @returns iprt statuscode.
194 * Will not return VERR_INTERRUPTED.
195 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
196 *
197 * @param pQueue The request queue.
198 * @param ppReq Where to store the pointer to the request.
199 * This will be NULL or a valid request pointer not matter what happens.
200 * @param cMillies Number of milliseconds to wait for the request to
201 * be completed. Use RT_INDEFINITE_WAIT to only
202 * wait till it's completed.
203 * @param pfnFunction Pointer to the function to call.
204 * @param cArgs Number of arguments following in the ellipsis.
205 * @param ... Function arguments.
206 *
207 * @remarks See remarks on RTReqCallV.
208 */
209RTDECL(int) RTReqCall(PRTREQQUEUE pQueue, PRTREQ *ppReq, RTMSINTERVAL cMillies, PFNRT pfnFunction, unsigned cArgs, ...)
210{
211 va_list va;
212 va_start(va, cArgs);
213 int rc = RTReqCallV(pQueue, ppReq, cMillies, RTREQFLAGS_IPRT_STATUS, pfnFunction, cArgs, va);
214 va_end(va);
215 return rc;
216}
217RT_EXPORT_SYMBOL(RTReqCall);
218
219
220/**
221 * Allocate and queue a call request to a void function.
222 *
223 * If it's desired to poll on the completion of the request set cMillies
224 * to 0 and use RTReqWait() to check for completion. In the other case
225 * use RT_INDEFINITE_WAIT.
226 * The returned request packet must be freed using RTReqFree().
227 *
228 * @returns iprt status code.
229 * Will not return VERR_INTERRUPTED.
230 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
231 *
232 * @param pQueue The request queue.
233 * @param ppReq Where to store the pointer to the request.
234 * This will be NULL or a valid request pointer not matter what happens.
235 * @param cMillies Number of milliseconds to wait for the request to
236 * be completed. Use RT_INDEFINITE_WAIT to only
237 * wait till it's completed.
238 * @param pfnFunction Pointer to the function to call.
239 * @param cArgs Number of arguments following in the ellipsis.
240 * @param ... Function arguments.
241 *
242 * @remarks See remarks on RTReqCallV.
243 */
244RTDECL(int) RTReqCallVoid(PRTREQQUEUE pQueue, PRTREQ *ppReq, RTMSINTERVAL cMillies, PFNRT pfnFunction, unsigned cArgs, ...)
245{
246 va_list va;
247 va_start(va, cArgs);
248 int rc = RTReqCallV(pQueue, ppReq, cMillies, RTREQFLAGS_VOID, pfnFunction, cArgs, va);
249 va_end(va);
250 return rc;
251}
252RT_EXPORT_SYMBOL(RTReqCallVoid);
253
254
255/**
256 * Allocate and queue a call request to a void function.
257 *
258 * If it's desired to poll on the completion of the request set cMillies
259 * to 0 and use RTReqWait() to check for completion. In the other case
260 * use RT_INDEFINITE_WAIT.
261 * The returned request packet must be freed using RTReqFree().
262 *
263 * @returns iprt status code.
264 * Will not return VERR_INTERRUPTED.
265 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
266 *
267 * @param pQueue The request queue.
268 * @param ppReq Where to store the pointer to the request.
269 * This will be NULL or a valid request pointer not matter what happens, unless fFlags
270 * contains RTREQFLAGS_NO_WAIT when it will be optional and always NULL.
271 * @param cMillies Number of milliseconds to wait for the request to
272 * be completed. Use RT_INDEFINITE_WAIT to only
273 * wait till it's completed.
274 * @param fFlags A combination of the RTREQFLAGS values.
275 * @param pfnFunction Pointer to the function to call.
276 * @param cArgs Number of arguments following in the ellipsis.
277 * @param ... Function arguments.
278 *
279 * @remarks See remarks on RTReqCallV.
280 */
281RTDECL(int) RTReqCallEx(PRTREQQUEUE pQueue, PRTREQ *ppReq, RTMSINTERVAL cMillies, unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, ...)
282{
283 va_list va;
284 va_start(va, cArgs);
285 int rc = RTReqCallV(pQueue, ppReq, cMillies, fFlags, pfnFunction, cArgs, va);
286 va_end(va);
287 return rc;
288}
289RT_EXPORT_SYMBOL(RTReqCallEx);
290
291
292/**
293 * Allocate and queue a call request.
294 *
295 * If it's desired to poll on the completion of the request set cMillies
296 * to 0 and use RTReqWait() to check for completion. In the other case
297 * use RT_INDEFINITE_WAIT.
298 * The returned request packet must be freed using RTReqFree().
299 *
300 * @returns iprt status code.
301 * Will not return VERR_INTERRUPTED.
302 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
303 *
304 * @param pQueue The request queue.
305 * @param ppReq Where to store the pointer to the request.
306 * This will be NULL or a valid request pointer not matter what happens, unless fFlags
307 * contains RTREQFLAGS_NO_WAIT when it will be optional and always NULL.
308 * @param cMillies Number of milliseconds to wait for the request to
309 * be completed. Use RT_INDEFINITE_WAIT to only
310 * wait till it's completed.
311 * @param fFlags A combination of the RTREQFLAGS values.
312 * @param pfnFunction Pointer to the function to call.
313 * @param cArgs Number of arguments following in the ellipsis.
314 * @param Args Variable argument vector.
315 *
316 * @remarks Caveats:
317 * - Do not pass anything which is larger than an uintptr_t.
318 * - 64-bit integers are larger than uintptr_t on 32-bit hosts.
319 * Pass integers > 32-bit by reference (pointers).
320 * - Don't use NULL since it should be the integer 0 in C++ and may
321 * therefore end up with garbage in the bits 63:32 on 64-bit
322 * hosts because 'int' is 32-bit.
323 * Use (void *)NULL or (uintptr_t)0 instead of NULL.
324 */
325RTDECL(int) RTReqCallV(PRTREQQUEUE pQueue, PRTREQ *ppReq, RTMSINTERVAL cMillies, unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, va_list Args)
326{
327 LogFlow(("RTReqCallV: cMillies=%d fFlags=%#x pfnFunction=%p cArgs=%d\n", cMillies, fFlags, pfnFunction, cArgs));
328
329 /*
330 * Check input.
331 */
332 if (!pfnFunction || !pQueue || (fFlags & ~(RTREQFLAGS_RETURN_MASK | RTREQFLAGS_NO_WAIT)))
333 {
334 AssertFailed();
335 return VERR_INVALID_PARAMETER;
336 }
337 if (!(fFlags & RTREQFLAGS_NO_WAIT) || ppReq)
338 {
339 Assert(ppReq);
340 *ppReq = NULL;
341 }
342 PRTREQ pReq = NULL;
343 if (cArgs * sizeof(uintptr_t) > sizeof(pReq->u.Internal.aArgs))
344 {
345 AssertMsgFailed(("cArg=%d\n", cArgs));
346 return VERR_TOO_MUCH_DATA;
347 }
348
349 /*
350 * Allocate request
351 */
352 int rc = RTReqAlloc(pQueue, &pReq, RTREQTYPE_INTERNAL);
353 if (rc != VINF_SUCCESS)
354 return rc;
355
356 /*
357 * Initialize the request data.
358 */
359 pReq->fFlags = fFlags;
360 pReq->u.Internal.pfn = pfnFunction;
361 pReq->u.Internal.cArgs = cArgs;
362 for (unsigned iArg = 0; iArg < cArgs; iArg++)
363 pReq->u.Internal.aArgs[iArg] = va_arg(Args, uintptr_t);
364
365 /*
366 * Queue the request and return.
367 */
368 rc = RTReqQueue(pReq, cMillies);
369 if ( rc != VINF_SUCCESS
370 && rc != VERR_TIMEOUT)
371 {
372 RTReqFree(pReq);
373 pReq = NULL;
374 }
375 if (!(fFlags & RTREQFLAGS_NO_WAIT))
376 {
377 *ppReq = pReq;
378 LogFlow(("RTReqCallV: returns %Rrc *ppReq=%p\n", rc, pReq));
379 }
380 else
381 LogFlow(("RTReqCallV: returns %Rrc\n", rc));
382 Assert(rc != VERR_INTERRUPTED);
383 return rc;
384}
385RT_EXPORT_SYMBOL(RTReqCallV);
386
387
388/**
389 * Joins the list pList with whatever is linked up at *pHead.
390 */
391static void vmr3ReqJoinFreeSub(volatile PRTREQ *ppHead, PRTREQ pList)
392{
393 for (unsigned cIterations = 0;; cIterations++)
394 {
395 PRTREQ pHead = ASMAtomicXchgPtrT(ppHead, pList, PRTREQ);
396 if (!pHead)
397 return;
398 PRTREQ pTail = pHead;
399 while (pTail->pNext)
400 pTail = pTail->pNext;
401 pTail->pNext = pList;
402 if (ASMAtomicCmpXchgPtr(ppHead, pHead, pList))
403 return;
404 pTail->pNext = NULL;
405 if (ASMAtomicCmpXchgPtr(ppHead, pHead, NULL))
406 return;
407 pList = pHead;
408 Assert(cIterations != 32);
409 Assert(cIterations != 64);
410 }
411}
412
413
414/**
415 * Joins the list pList with whatever is linked up at *pHead.
416 */
417static void vmr3ReqJoinFree(PRTREQQUEUE pQueue, PRTREQ pList)
418{
419 /*
420 * Split the list if it's too long.
421 */
422 unsigned cReqs = 1;
423 PRTREQ pTail = pList;
424 while (pTail->pNext)
425 {
426 if (cReqs++ > 25)
427 {
428 const uint32_t i = pQueue->iReqFree;
429 vmr3ReqJoinFreeSub(&pQueue->apReqFree[(i + 2) % RT_ELEMENTS(pQueue->apReqFree)], pTail->pNext);
430
431 pTail->pNext = NULL;
432 vmr3ReqJoinFreeSub(&pQueue->apReqFree[(i + 2 + (i == pQueue->iReqFree)) % RT_ELEMENTS(pQueue->apReqFree)], pTail->pNext);
433 return;
434 }
435 pTail = pTail->pNext;
436 }
437 vmr3ReqJoinFreeSub(&pQueue->apReqFree[(pQueue->iReqFree + 2) % RT_ELEMENTS(pQueue->apReqFree)], pList);
438}
439
440
441/**
442 * Allocates a request packet.
443 *
444 * The caller allocates a request packet, fills in the request data
445 * union and queues the request.
446 *
447 * @returns iprt status code.
448 *
449 * @param pQueue The request queue.
450 * @param ppReq Where to store the pointer to the allocated packet.
451 * @param enmType Package type.
452 */
453RTDECL(int) RTReqAlloc(PRTREQQUEUE pQueue, PRTREQ *ppReq, RTREQTYPE enmType)
454{
455 RT_EXPORT_SYMBOL(RTReqAlloc);
456 /*
457 * Validate input.
458 */
459 if ( enmType < RTREQTYPE_INVALID
460 || enmType > RTREQTYPE_MAX)
461 {
462 AssertMsgFailed(("Invalid package type %d valid range %d-%d inclusively.\n",
463 enmType, RTREQTYPE_INVALID + 1, RTREQTYPE_MAX - 1));
464 return VERR_RT_REQUEST_INVALID_TYPE;
465 }
466
467 /*
468 * Try get a recycled packet.
469 * While this could all be solved with a single list with a lock, it's a sport
470 * of mine to avoid locks.
471 */
472 int cTries = RT_ELEMENTS(pQueue->apReqFree) * 2;
473 while (--cTries >= 0)
474 {
475 PRTREQ volatile *ppHead = &pQueue->apReqFree[ASMAtomicIncU32(&pQueue->iReqFree) % RT_ELEMENTS(pQueue->apReqFree)];
476#if 0 /* sad, but this won't work safely because the reading of pReq->pNext. */
477 PRTREQ pNext = NULL;
478 PRTREQ pReq = *ppHead;
479 if ( pReq
480 && !ASMAtomicCmpXchgPtr(ppHead, (pNext = pReq->pNext), pReq)
481 && (pReq = *ppHead)
482 && !ASMAtomicCmpXchgPtr(ppHead, (pNext = pReq->pNext), pReq))
483 pReq = NULL;
484 if (pReq)
485 {
486 Assert(pReq->pNext == pNext); NOREF(pReq);
487#else
488 PRTREQ pReq = ASMAtomicXchgPtrT(ppHead, NULL, PRTREQ);
489 if (pReq)
490 {
491 PRTREQ pNext = pReq->pNext;
492 if ( pNext
493 && !ASMAtomicCmpXchgPtr(ppHead, pNext, NULL))
494 {
495 vmr3ReqJoinFree(pQueue, pReq->pNext);
496 }
497#endif
498 ASMAtomicDecU32(&pQueue->cReqFree);
499
500 /*
501 * Make sure the event sem is not signaled.
502 */
503 if (!pReq->fEventSemClear)
504 {
505 int rc = RTSemEventWait(pReq->EventSem, 0);
506 if (rc != VINF_SUCCESS && rc != VERR_TIMEOUT)
507 {
508 /*
509 * This shall not happen, but if it does we'll just destroy
510 * the semaphore and create a new one.
511 */
512 AssertMsgFailed(("rc=%Rrc from RTSemEventWait(%#x).\n", rc, pReq->EventSem));
513 RTSemEventDestroy(pReq->EventSem);
514 rc = RTSemEventCreate(&pReq->EventSem);
515 AssertRC(rc);
516 if (rc != VINF_SUCCESS)
517 return rc;
518 }
519 pReq->fEventSemClear = true;
520 }
521 else
522 Assert(RTSemEventWait(pReq->EventSem, 0) == VERR_TIMEOUT);
523
524 /*
525 * Initialize the packet and return it.
526 */
527 Assert(pReq->enmType == RTREQTYPE_INVALID);
528 Assert(pReq->enmState == RTREQSTATE_FREE);
529 Assert(pReq->pQueue == pQueue);
530 ASMAtomicXchgSize(&pReq->pNext, NULL);
531 pReq->enmState = RTREQSTATE_ALLOCATED;
532 pReq->iStatus = VERR_RT_REQUEST_STATUS_STILL_PENDING;
533 pReq->fFlags = RTREQFLAGS_IPRT_STATUS;
534 pReq->enmType = enmType;
535
536 *ppReq = pReq;
537 LogFlow(("RTReqAlloc: returns VINF_SUCCESS *ppReq=%p recycled\n", pReq));
538 return VINF_SUCCESS;
539 }
540 }
541
542 /*
543 * Ok allocate one.
544 */
545 PRTREQ pReq = (PRTREQ)RTMemAllocZ(sizeof(*pReq));
546 if (!pReq)
547 return VERR_NO_MEMORY;
548
549 /*
550 * Create the semaphore.
551 */
552 int rc = RTSemEventCreate(&pReq->EventSem);
553 AssertRC(rc);
554 if (rc != VINF_SUCCESS)
555 {
556 RTMemFree(pReq);
557 return rc;
558 }
559
560 /*
561 * Initialize the packet and return it.
562 */
563 pReq->pNext = NULL;
564 pReq->pQueue = pQueue;
565 pReq->enmState = RTREQSTATE_ALLOCATED;
566 pReq->iStatus = VERR_RT_REQUEST_STATUS_STILL_PENDING;
567 pReq->fEventSemClear = true;
568 pReq->fFlags = RTREQFLAGS_IPRT_STATUS;
569 pReq->enmType = enmType;
570
571 *ppReq = pReq;
572 LogFlow(("RTReqAlloc: returns VINF_SUCCESS *ppReq=%p new\n", pReq));
573 return VINF_SUCCESS;
574}
575
576
577/**
578 * Free a request packet.
579 *
580 * @returns iprt status code.
581 *
582 * @param pReq Package to free.
583 * @remark The request packet must be in allocated or completed state!
584 */
585RTDECL(int) RTReqFree(PRTREQ pReq)
586{
587 /*
588 * Ignore NULL (all free functions should do this imho).
589 */
590 if (!pReq)
591 return VINF_SUCCESS;
592
593
594 /*
595 * Check packet state.
596 */
597 switch (pReq->enmState)
598 {
599 case RTREQSTATE_ALLOCATED:
600 case RTREQSTATE_COMPLETED:
601 break;
602 default:
603 AssertMsgFailed(("Invalid state %d!\n", pReq->enmState));
604 return VERR_RT_REQUEST_STATE;
605 }
606
607 /*
608 * Make it a free packet and put it into one of the free packet lists.
609 */
610 pReq->enmState = RTREQSTATE_FREE;
611 pReq->iStatus = VERR_RT_REQUEST_STATUS_FREED;
612 pReq->enmType = RTREQTYPE_INVALID;
613
614 PRTREQQUEUE pQueue = pReq->pQueue;
615 if (pQueue->cReqFree < 128)
616 {
617 ASMAtomicIncU32(&pQueue->cReqFree);
618 PRTREQ volatile *ppHead = &pQueue->apReqFree[ASMAtomicIncU32(&pQueue->iReqFree) % RT_ELEMENTS(pQueue->apReqFree)];
619 PRTREQ pNext;
620 do
621 {
622 pNext = *ppHead;
623 ASMAtomicWritePtr(&pReq->pNext, pNext);
624 } while (!ASMAtomicCmpXchgPtr(ppHead, pReq, pNext));
625 }
626 else
627 {
628 RTSemEventDestroy(pReq->EventSem);
629 RTMemFree(pReq);
630 }
631 return VINF_SUCCESS;
632}
633RT_EXPORT_SYMBOL(RTReqFree);
634
635
636/**
637 * Queue a request.
638 *
639 * The quest must be allocated using RTReqAlloc() and contain
640 * all the required data.
641 * If it's desired to poll on the completion of the request set cMillies
642 * to 0 and use RTReqWait() to check for completion. In the other case
643 * use RT_INDEFINITE_WAIT.
644 *
645 * @returns iprt status code.
646 * Will not return VERR_INTERRUPTED.
647 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
648 *
649 * @param pReq The request to queue.
650 * @param cMillies Number of milliseconds to wait for the request to
651 * be completed. Use RT_INDEFINITE_WAIT to only
652 * wait till it's completed.
653 */
654RTDECL(int) RTReqQueue(PRTREQ pReq, RTMSINTERVAL cMillies)
655{
656 LogFlow(("RTReqQueue: pReq=%p cMillies=%d\n", pReq, cMillies));
657 /*
658 * Verify the supplied package.
659 */
660 if (pReq->enmState != RTREQSTATE_ALLOCATED)
661 {
662 AssertMsgFailed(("Invalid state %d\n", pReq->enmState));
663 return VERR_RT_REQUEST_STATE;
664 }
665 if ( !pReq->pQueue
666 || pReq->pNext
667 || !pReq->EventSem)
668 {
669 AssertMsgFailed(("Invalid request package! Anyone cooking their own packages???\n"));
670 return VERR_RT_REQUEST_INVALID_PACKAGE;
671 }
672 if ( pReq->enmType < RTREQTYPE_INVALID
673 || pReq->enmType > RTREQTYPE_MAX)
674 {
675 AssertMsgFailed(("Invalid package type %d valid range %d-%d inclusively. This was verified on alloc too...\n",
676 pReq->enmType, RTREQTYPE_INVALID + 1, RTREQTYPE_MAX - 1));
677 return VERR_RT_REQUEST_INVALID_TYPE;
678 }
679
680 int rc = VINF_SUCCESS;
681 /*
682 * Insert it.
683 */
684 PRTREQQUEUE pQueue = ((RTREQ volatile *)pReq)->pQueue; /* volatile paranoia */
685 unsigned fFlags = ((RTREQ volatile *)pReq)->fFlags; /* volatile paranoia */
686 pReq->enmState = RTREQSTATE_QUEUED;
687 PRTREQ pNext;
688 do
689 {
690 pNext = pQueue->pReqs;
691 pReq->pNext = pNext;
692 ASMAtomicWriteBool(&pQueue->fBusy, true);
693 } while (!ASMAtomicCmpXchgPtr(&pQueue->pReqs, pReq, pNext));
694
695 /*
696 * Notify queue thread.
697 */
698 RTSemEventSignal(pQueue->EventSem);
699
700 /*
701 * Wait and return.
702 */
703 if (!(fFlags & RTREQFLAGS_NO_WAIT))
704 rc = RTReqWait(pReq, cMillies);
705 LogFlow(("RTReqQueue: returns %Rrc\n", rc));
706 return rc;
707}
708RT_EXPORT_SYMBOL(RTReqQueue);
709
710
711/**
712 * Wait for a request to be completed.
713 *
714 * @returns iprt status code.
715 * Will not return VERR_INTERRUPTED.
716 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
717 *
718 * @param pReq The request to wait for.
719 * @param cMillies Number of milliseconds to wait.
720 * Use RT_INDEFINITE_WAIT to only wait till it's completed.
721 */
722RTDECL(int) RTReqWait(PRTREQ pReq, RTMSINTERVAL cMillies)
723{
724 LogFlow(("RTReqWait: pReq=%p cMillies=%d\n", pReq, cMillies));
725
726 /*
727 * Verify the supplied package.
728 */
729 if ( pReq->enmState != RTREQSTATE_QUEUED
730 && pReq->enmState != RTREQSTATE_PROCESSING
731 && pReq->enmState != RTREQSTATE_COMPLETED)
732 {
733 AssertMsgFailed(("Invalid state %d\n", pReq->enmState));
734 return VERR_RT_REQUEST_STATE;
735 }
736 if ( !pReq->pQueue
737 || !pReq->EventSem)
738 {
739 AssertMsgFailed(("Invalid request package! Anyone cooking their own packages???\n"));
740 return VERR_RT_REQUEST_INVALID_PACKAGE;
741 }
742 if ( pReq->enmType < RTREQTYPE_INVALID
743 || pReq->enmType > RTREQTYPE_MAX)
744 {
745 AssertMsgFailed(("Invalid package type %d valid range %d-%d inclusively. This was verified on alloc and queue too...\n",
746 pReq->enmType, RTREQTYPE_INVALID + 1, RTREQTYPE_MAX - 1));
747 return VERR_RT_REQUEST_INVALID_TYPE;
748 }
749
750 /*
751 * Wait on the package.
752 */
753 int rc;
754 if (cMillies != RT_INDEFINITE_WAIT)
755 rc = RTSemEventWait(pReq->EventSem, cMillies);
756 else
757 {
758 do
759 {
760 rc = RTSemEventWait(pReq->EventSem, RT_INDEFINITE_WAIT);
761 Assert(rc != VERR_TIMEOUT);
762 } while (pReq->enmState != RTREQSTATE_COMPLETED);
763 }
764 if (rc == VINF_SUCCESS)
765 ASMAtomicXchgSize(&pReq->fEventSemClear, true);
766 if (pReq->enmState == RTREQSTATE_COMPLETED)
767 rc = VINF_SUCCESS;
768 LogFlow(("RTReqWait: returns %Rrc\n", rc));
769 Assert(rc != VERR_INTERRUPTED);
770 return rc;
771}
772RT_EXPORT_SYMBOL(RTReqWait);
773
774
775/**
776 * Process one request.
777 *
778 * @returns IPRT status code.
779 *
780 * @param pReq Request packet to process.
781 */
782static int rtReqProcessOne(PRTREQ pReq)
783{
784 LogFlow(("rtReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
785
786 /*
787 * Process the request.
788 */
789 Assert(pReq->enmState == RTREQSTATE_QUEUED);
790 pReq->enmState = RTREQSTATE_PROCESSING;
791 int rcRet = VINF_SUCCESS; /* the return code of this function. */
792 int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
793 switch (pReq->enmType)
794 {
795 /*
796 * A packed down call frame.
797 */
798 case RTREQTYPE_INTERNAL:
799 {
800 uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
801 union
802 {
803 PFNRT pfn;
804 DECLCALLBACKMEMBER(int, pfn00)(void);
805 DECLCALLBACKMEMBER(int, pfn01)(uintptr_t);
806 DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t);
807 DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t);
808 DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
809 DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
810 DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
811 DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
812 DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
813 DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
814 DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
815 DECLCALLBACKMEMBER(int, pfn11)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
816 DECLCALLBACKMEMBER(int, pfn12)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
817 } u;
818 u.pfn = pReq->u.Internal.pfn;
819#ifndef RT_ARCH_X86
820 switch (pReq->u.Internal.cArgs)
821 {
822 case 0: rcRet = u.pfn00(); break;
823 case 1: rcRet = u.pfn01(pauArgs[0]); break;
824 case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
825 case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
826 case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
827 case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
828 case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
829 case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
830 case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
831 case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
832 case 10: rcRet = u.pfn10(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9]); break;
833 case 11: rcRet = u.pfn11(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9], pauArgs[10]); break;
834 case 12: rcRet = u.pfn12(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9], pauArgs[10], pauArgs[11]); break;
835 default:
836 AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
837 rcRet = rcReq = VERR_INTERNAL_ERROR;
838 break;
839 }
840#else /* RT_ARCH_X86 */
841 size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
842# ifdef __GNUC__
843 __asm__ __volatile__("movl %%esp, %%edx\n\t"
844 "subl %2, %%esp\n\t"
845 "andl $0xfffffff0, %%esp\n\t"
846 "shrl $2, %2\n\t"
847 "movl %%esp, %%edi\n\t"
848 "rep movsl\n\t"
849 "movl %%edx, %%edi\n\t"
850 "call *%%eax\n\t"
851 "mov %%edi, %%esp\n\t"
852 : "=a" (rcRet),
853 "=S" (pauArgs),
854 "=c" (cbArgs)
855 : "0" (u.pfn),
856 "1" (pauArgs),
857 "2" (cbArgs)
858 : "edi", "edx");
859# else
860 __asm
861 {
862 xor edx, edx /* just mess it up. */
863 mov eax, u.pfn
864 mov ecx, cbArgs
865 shr ecx, 2
866 mov esi, pauArgs
867 mov ebx, esp
868 sub esp, cbArgs
869 and esp, 0xfffffff0
870 mov edi, esp
871 rep movsd
872 call eax
873 mov esp, ebx
874 mov rcRet, eax
875 }
876# endif
877#endif /* RT_ARCH_X86 */
878 if ((pReq->fFlags & (RTREQFLAGS_RETURN_MASK)) == RTREQFLAGS_VOID)
879 rcRet = VINF_SUCCESS;
880 rcReq = rcRet;
881 break;
882 }
883
884 default:
885 AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
886 rcReq = VERR_NOT_IMPLEMENTED;
887 break;
888 }
889
890 /*
891 * Complete the request.
892 */
893 pReq->iStatus = rcReq;
894 pReq->enmState = RTREQSTATE_COMPLETED;
895 if (pReq->fFlags & RTREQFLAGS_NO_WAIT)
896 {
897 /* Free the packet, nobody is waiting. */
898 LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - freeing it\n",
899 pReq, rcReq, rcRet));
900 RTReqFree(pReq);
901 }
902 else
903 {
904 /* Notify the waiter and him free up the packet. */
905 LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - notifying waiting thread\n",
906 pReq, rcReq, rcRet));
907 ASMAtomicXchgSize(&pReq->fEventSemClear, false);
908 int rc2 = RTSemEventSignal(pReq->EventSem);
909 if (rc2 != VINF_SUCCESS)
910 {
911 AssertRC(rc2);
912 rcRet = rc2;
913 }
914 }
915 return rcRet;
916}
917
918
919/**
920 * Checks if the queue is busy or not.
921 *
922 * The caller is responsible for dealing with any concurrent submitts.
923 *
924 * @returns true if busy, false if idle.
925 * @param pQueue The queue.
926 */
927RTDECL(bool) RTReqIsBusy(PRTREQQUEUE pQueue)
928{
929 AssertPtrReturn(pQueue, false);
930
931 if (ASMAtomicReadBool(&pQueue->fBusy))
932 return true;
933 if (ASMAtomicReadPtrT(&pQueue->pReqs, PRTREQ) != NULL)
934 return true;
935 if (ASMAtomicReadBool(&pQueue->fBusy))
936 return true;
937 return false;
938}
939RT_EXPORT_SYMBOL(RTReqIsBusy);
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