VirtualBox

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

Last change on this file since 7282 was 7169, checked in by vboxsync, 17 years ago

Doxygen fixes.

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