VirtualBox

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

Last change on this file since 26562 was 26562, checked in by vboxsync, 15 years ago

*: Added svn:keywords where missing.

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