VirtualBox

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

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

grr!

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