VirtualBox

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

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

various stray files -> common/misc/

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