VirtualBox

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

Last change on this file since 9230 was 8245, checked in by vboxsync, 17 years ago

rebranding: IPRT files again.

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