VirtualBox

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

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

iprt/req.h: Added RTReqIsBusy.

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