VirtualBox

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

Last change on this file since 18188 was 14012, checked in by vboxsync, 16 years ago

pReq may be invalid after queuing it (RTREQFLAGS_NO_WAIT), similar to r27120.

  • Property svn:eol-style set to native
File size: 30.8 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; /** @todo r=bird: we're dropping requests here! Add 2nd queue that can hold them. (will fix when writing a testcase) */
172 }
173 }
174
175 LogFlow(("RTReqProcess: returns %Rrc\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 %Rrc *ppReq=%p\n", rc, pReq));
359 }
360 else
361 LogFlow(("RTReqCallV: returns %Rrc\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) % RT_ELEMENTS(pQueue->apReqFree)], pTail->pNext);
409
410 pTail->pNext = NULL;
411 vmr3ReqJoinFreeSub(&pQueue->apReqFree[(i + 2 + (i == pQueue->iReqFree)) % RT_ELEMENTS(pQueue->apReqFree)], pTail->pNext);
412 return;
413 }
414 pTail = pTail->pNext;
415 }
416 vmr3ReqJoinFreeSub(&pQueue->apReqFree[(pQueue->iReqFree + 2) % RT_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 = RT_ELEMENTS(pQueue->apReqFree) * 2;
451 while (--cTries >= 0)
452 {
453 PRTREQ volatile *ppHead = &pQueue->apReqFree[ASMAtomicIncU32(&pQueue->iReqFree) % RT_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=%Rrc 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) % RT_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 PRTREQQUEUE pQueue = ((RTREQ volatile *)pReq)->pQueue; /* volatile paranoia */
662 unsigned fFlags = ((RTREQ volatile *)pReq)->fFlags; /* volatile paranoia */
663 pReq->enmState = RTREQSTATE_QUEUED;
664 PRTREQ pNext;
665 do
666 {
667 pNext = pQueue->pReqs;
668 pReq->pNext = pNext;
669 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pQueue->pReqs, (void *)pReq, (void *)pNext));
670
671 /*
672 * Notify queue thread.
673 */
674 RTSemEventSignal(pQueue->EventSem);
675
676 /*
677 * Wait and return.
678 */
679 if (!(fFlags & RTREQFLAGS_NO_WAIT))
680 rc = RTReqWait(pReq, cMillies);
681 LogFlow(("RTReqQueue: returns %Rrc\n", rc));
682 return rc;
683}
684
685
686/**
687 * Wait for a request to be completed.
688 *
689 * @returns iprt status code.
690 * Will not return VERR_INTERRUPTED.
691 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
692 *
693 * @param pReq The request to wait for.
694 * @param cMillies Number of milliseconds to wait.
695 * Use RT_INDEFINITE_WAIT to only wait till it's completed.
696 */
697RTDECL(int) RTReqWait(PRTREQ pReq, unsigned cMillies)
698{
699 LogFlow(("RTReqWait: pReq=%p cMillies=%d\n", pReq, cMillies));
700
701 /*
702 * Verify the supplied package.
703 */
704 if ( pReq->enmState != RTREQSTATE_QUEUED
705 && pReq->enmState != RTREQSTATE_PROCESSING
706 && pReq->enmState != RTREQSTATE_COMPLETED)
707 {
708 AssertMsgFailed(("Invalid state %d\n", pReq->enmState));
709 return VERR_RT_REQUEST_STATE;
710 }
711 if ( !pReq->pQueue
712 || !pReq->EventSem)
713 {
714 AssertMsgFailed(("Invalid request package! Anyone cooking their own packages???\n"));
715 return VERR_RT_REQUEST_INVALID_PACKAGE;
716 }
717 if ( pReq->enmType < RTREQTYPE_INVALID
718 || pReq->enmType > RTREQTYPE_MAX)
719 {
720 AssertMsgFailed(("Invalid package type %d valid range %d-%d inclusivly. This was verified on alloc and queue too...\n",
721 pReq->enmType, RTREQTYPE_INVALID + 1, RTREQTYPE_MAX - 1));
722 return VERR_RT_REQUEST_INVALID_TYPE;
723 }
724
725 /*
726 * Wait on the package.
727 */
728 int rc;
729 if (cMillies != RT_INDEFINITE_WAIT)
730 rc = RTSemEventWait(pReq->EventSem, cMillies);
731 else
732 {
733 do
734 {
735 rc = RTSemEventWait(pReq->EventSem, RT_INDEFINITE_WAIT);
736 Assert(rc != VERR_TIMEOUT);
737 } while (pReq->enmState != RTREQSTATE_COMPLETED);
738 }
739 if (rc == VINF_SUCCESS)
740 ASMAtomicXchgSize(&pReq->fEventSemClear, true);
741 if (pReq->enmState == RTREQSTATE_COMPLETED)
742 rc = VINF_SUCCESS;
743 LogFlow(("RTReqWait: returns %Rrc\n", rc));
744 Assert(rc != VERR_INTERRUPTED);
745 return rc;
746}
747
748
749/**
750 * Process one request.
751 *
752 * @returns IPRT status code.
753 *
754 * @param pReq Request packet to process.
755 */
756static int rtReqProcessOne(PRTREQ pReq)
757{
758 LogFlow(("rtReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
759
760 /*
761 * Process the request.
762 */
763 Assert(pReq->enmState == RTREQSTATE_QUEUED);
764 pReq->enmState = RTREQSTATE_PROCESSING;
765 int rcRet = VINF_SUCCESS; /* the return code of this function. */
766 int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
767 switch (pReq->enmType)
768 {
769 /*
770 * A packed down call frame.
771 */
772 case RTREQTYPE_INTERNAL:
773 {
774 uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
775 union
776 {
777 PFNRT pfn;
778 DECLCALLBACKMEMBER(int, pfn00)(void);
779 DECLCALLBACKMEMBER(int, pfn01)(uintptr_t);
780 DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t);
781 DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t);
782 DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
783 DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
784 DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
785 DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
786 DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
787 DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
788 DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
789 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);
790 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);
791 } u;
792 u.pfn = pReq->u.Internal.pfn;
793#ifdef RT_ARCH_AMD64
794 switch (pReq->u.Internal.cArgs)
795 {
796 case 0: rcRet = u.pfn00(); break;
797 case 1: rcRet = u.pfn01(pauArgs[0]); break;
798 case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
799 case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
800 case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
801 case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
802 case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
803 case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
804 case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
805 case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
806 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;
807 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;
808 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;
809 default:
810 AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
811 rcRet = rcReq = VERR_INTERNAL_ERROR;
812 break;
813 }
814#else /* x86: */
815 size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
816# ifdef __GNUC__
817 __asm__ __volatile__("movl %%esp, %%edx\n\t"
818 "subl %2, %%esp\n\t"
819 "andl $0xfffffff0, %%esp\n\t"
820 "shrl $2, %2\n\t"
821 "movl %%esp, %%edi\n\t"
822 "rep movsl\n\t"
823 "movl %%edx, %%edi\n\t"
824 "call *%%eax\n\t"
825 "mov %%edi, %%esp\n\t"
826 : "=a" (rcRet),
827 "=S" (pauArgs),
828 "=c" (cbArgs)
829 : "0" (u.pfn),
830 "1" (pauArgs),
831 "2" (cbArgs)
832 : "edi", "edx");
833# else
834 __asm
835 {
836 xor edx, edx /* just mess it up. */
837 mov eax, u.pfn
838 mov ecx, cbArgs
839 shr ecx, 2
840 mov esi, pauArgs
841 mov ebx, esp
842 sub esp, cbArgs
843 and esp, 0xfffffff0
844 mov edi, esp
845 rep movsd
846 call eax
847 mov esp, ebx
848 mov rcRet, eax
849 }
850# endif
851#endif /* x86 */
852 if ((pReq->fFlags & (RTREQFLAGS_RETURN_MASK)) == RTREQFLAGS_VOID)
853 rcRet = VINF_SUCCESS;
854 rcReq = rcRet;
855 break;
856 }
857
858 default:
859 AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
860 rcReq = VERR_NOT_IMPLEMENTED;
861 break;
862 }
863
864 /*
865 * Complete the request.
866 */
867 pReq->iStatus = rcReq;
868 pReq->enmState = RTREQSTATE_COMPLETED;
869 if (pReq->fFlags & RTREQFLAGS_NO_WAIT)
870 {
871 /* Free the packet, nobody is waiting. */
872 LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - freeing it\n",
873 pReq, rcReq, rcRet));
874 RTReqFree(pReq);
875 }
876 else
877 {
878 /* Notify the waiter and him free up the packet. */
879 LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - notifying waiting thread\n",
880 pReq, rcReq, rcRet));
881 ASMAtomicXchgSize(&pReq->fEventSemClear, false);
882 int rc2 = RTSemEventSignal(pReq->EventSem);
883 if (rc2 != VINF_SUCCESS)
884 {
885 AssertRC(rc2);
886 rcRet = rc2;
887 }
888 }
889 return rcRet;
890}
891
892
893
894
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