VirtualBox

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

Last change on this file since 21337 was 21337, checked in by vboxsync, 15 years ago

IPRT,HostDrv,AddDrv: Export public IPRT symbols for the linux kernel (pain).

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