VirtualBox

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

Last change on this file since 911 was 633, checked in by vboxsync, 18 years ago

Good job!

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