VirtualBox

source: vbox/trunk/src/VBox/Runtime/req.cpp@ 628

Last change on this file since 628 was 622, checked in by vboxsync, 18 years ago

export to OSE

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