VirtualBox

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

Last change on this file since 74762 was 69111, checked in by vboxsync, 7 years ago

(C) year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 17.4 KB
Line 
1/* $Id: req.cpp 69111 2017-10-17 14:26:02Z vboxsync $ */
2/** @file
3 * IPRT - Request packets
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
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
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/req.h>
32#include "internal/iprt.h"
33
34#include <iprt/assert.h>
35#include <iprt/asm.h>
36#include <iprt/string.h>
37#include <iprt/time.h>
38#include <iprt/semaphore.h>
39#include <iprt/thread.h>
40#include <iprt/log.h>
41#include <iprt/mem.h>
42
43#include "internal/req.h"
44#include "internal/magics.h"
45
46
47/*********************************************************************************************************************************
48* Internal Functions *
49*********************************************************************************************************************************/
50
51
52/**
53 * Allocate a new request from the heap.
54 *
55 * @returns IPRT status code.
56 * @param enmType The reques type.
57 * @param fPoolOrQueue The owner type.
58 * @param pvOwner The owner.
59 * @param phReq Where to return the request handle.
60 */
61DECLHIDDEN(int) rtReqAlloc(RTREQTYPE enmType, bool fPoolOrQueue, void *pvOwner, PRTREQ *phReq)
62{
63 PRTREQ pReq = (PRTREQ)RTMemAllocZ(sizeof(*pReq));
64 if (RT_UNLIKELY(!pReq))
65 return VERR_NO_MEMORY;
66
67 /*
68 * Create the semaphore used for waiting.
69 */
70 int rc = RTSemEventCreate(&pReq->EventSem);
71 AssertRCReturnStmt(rc, RTMemFree(pReq), rc);
72
73 /*
74 * Initialize the packet and return it.
75 */
76 pReq->u32Magic = RTREQ_MAGIC;
77 pReq->fEventSemClear = true;
78 pReq->fSignalPushBack = true;
79 pReq->fPoolOrQueue = fPoolOrQueue;
80 pReq->iStatusX = VERR_RT_REQUEST_STATUS_STILL_PENDING;
81 pReq->enmState = RTREQSTATE_ALLOCATED;
82 pReq->pNext = NULL;
83 pReq->uOwner.pv = pvOwner;
84 pReq->fFlags = RTREQFLAGS_IPRT_STATUS;
85 pReq->enmType = enmType;
86 pReq->cRefs = 1;
87
88 *phReq = pReq;
89 return VINF_SUCCESS;
90}
91
92
93/**
94 * Re-initializes a request when it's being recycled.
95 *
96 * @returns IRPT status code, the request is freed on failure.
97 * @param pReq The request.
98 * @param enmType The request type.
99 */
100DECLHIDDEN(int) rtReqReInit(PRTREQINT pReq, RTREQTYPE enmType)
101{
102 Assert(pReq->u32Magic == RTREQ_MAGIC);
103 Assert(pReq->enmType == RTREQTYPE_INVALID);
104 Assert(pReq->enmState == RTREQSTATE_FREE);
105 Assert(pReq->cRefs == 0);
106
107 /*
108 * Make sure the event sem is not signaled.
109 */
110 if (!pReq->fEventSemClear)
111 {
112 int rc = RTSemEventWait(pReq->EventSem, 0);
113 if (rc != VINF_SUCCESS && rc != VERR_TIMEOUT)
114 {
115 /*
116 * This shall not happen, but if it does we'll just destroy
117 * the semaphore and create a new one.
118 */
119 AssertMsgFailed(("rc=%Rrc from RTSemEventWait(%#x).\n", rc, pReq->EventSem));
120 RTSemEventDestroy(pReq->EventSem);
121 rc = RTSemEventCreate(&pReq->EventSem);
122 if (RT_FAILURE(rc))
123 {
124 AssertRC(rc);
125 pReq->EventSem = NIL_RTSEMEVENT;
126 rtReqFreeIt(pReq);
127 return rc;
128 }
129 }
130 pReq->fEventSemClear = true;
131 }
132 else
133 Assert(RTSemEventWait(pReq->EventSem, 0) == VERR_TIMEOUT);
134
135 /*
136 * Initialize the packet and return it.
137 */
138 ASMAtomicWriteNullPtr(&pReq->pNext);
139 pReq->iStatusX = VERR_RT_REQUEST_STATUS_STILL_PENDING;
140 pReq->enmState = RTREQSTATE_ALLOCATED;
141 pReq->fFlags = RTREQFLAGS_IPRT_STATUS;
142 pReq->enmType = enmType;
143 pReq->cRefs = 1;
144 return VINF_SUCCESS;
145}
146
147
148RTDECL(uint32_t) RTReqRetain(PRTREQ hReq)
149{
150 PRTREQINT pReq = hReq;
151 AssertPtrReturn(pReq, UINT32_MAX);
152 AssertReturn(pReq->u32Magic == RTREQ_MAGIC, UINT32_MAX);
153
154 return ASMAtomicIncU32(&pReq->cRefs);
155}
156RT_EXPORT_SYMBOL(RTReqRetain);
157
158
159/**
160 * Frees a request.
161 *
162 * @param pReq The request.
163 */
164DECLHIDDEN(void) rtReqFreeIt(PRTREQINT pReq)
165{
166 Assert(pReq->u32Magic == RTREQ_MAGIC);
167 Assert(pReq->cRefs == 0);
168
169 pReq->u32Magic = RTREQ_MAGIC_DEAD;
170 RTSemEventDestroy(pReq->EventSem);
171 pReq->EventSem = NIL_RTSEMEVENT;
172 RTSemEventMultiDestroy(pReq->hPushBackEvt);
173 pReq->hPushBackEvt = NIL_RTSEMEVENTMULTI;
174 RTMemFree(pReq);
175}
176
177
178RTDECL(uint32_t) RTReqRelease(PRTREQ hReq)
179{
180 /*
181 * Ignore NULL and validate the request.
182 */
183 if (!hReq)
184 return 0;
185 PRTREQINT pReq = hReq;
186 AssertPtrReturn(pReq, UINT32_MAX);
187 AssertReturn(pReq->u32Magic == RTREQ_MAGIC, UINT32_MAX);
188
189 /*
190 * Drop a reference, recycle the request when we reach 0.
191 */
192 uint32_t cRefs = ASMAtomicDecU32(&pReq->cRefs);
193 if (cRefs == 0)
194 {
195 /*
196 * Check packet state.
197 */
198 switch (pReq->enmState)
199 {
200 case RTREQSTATE_ALLOCATED:
201 case RTREQSTATE_COMPLETED:
202 break;
203 default:
204 AssertMsgFailed(("Invalid state %d!\n", pReq->enmState));
205 return 0;
206 }
207
208 /*
209 * Make it a free packet and put it into one of the free packet lists.
210 */
211 pReq->enmState = RTREQSTATE_FREE;
212 pReq->iStatusX = VERR_RT_REQUEST_STATUS_FREED;
213 pReq->enmType = RTREQTYPE_INVALID;
214
215 bool fRecycled;
216 if (pReq->fPoolOrQueue)
217 fRecycled = rtReqPoolRecycle(pReq->uOwner.hPool, pReq);
218 else
219 fRecycled = rtReqQueueRecycle(pReq->uOwner.hQueue, pReq);
220 if (!fRecycled)
221 rtReqFreeIt(pReq);
222 }
223
224 return cRefs;
225}
226RT_EXPORT_SYMBOL(RTReqRelease);
227
228
229RTDECL(int) RTReqSubmit(PRTREQ hReq, RTMSINTERVAL cMillies)
230{
231 LogFlow(("RTReqSubmit: hReq=%p cMillies=%d\n", hReq, cMillies));
232
233 /*
234 * Verify the supplied package.
235 */
236 PRTREQINT pReq = hReq;
237 AssertPtrReturn(pReq, VERR_INVALID_HANDLE);
238 AssertReturn(pReq->u32Magic == RTREQ_MAGIC, VERR_INVALID_HANDLE);
239 AssertMsgReturn(pReq->enmState == RTREQSTATE_ALLOCATED, ("%d\n", pReq->enmState), VERR_RT_REQUEST_STATE);
240 AssertMsgReturn(pReq->uOwner.hQueue && !pReq->pNext && pReq->EventSem != NIL_RTSEMEVENT,
241 ("Invalid request package! Anyone cooking their own packages???\n"),
242 VERR_RT_REQUEST_INVALID_PACKAGE);
243 AssertMsgReturn(pReq->enmType > RTREQTYPE_INVALID && pReq->enmType < RTREQTYPE_MAX,
244 ("Invalid package type %d valid range %d-%d inclusively. This was verified on alloc too...\n",
245 pReq->enmType, RTREQTYPE_INVALID + 1, RTREQTYPE_MAX - 1),
246 VERR_RT_REQUEST_INVALID_TYPE);
247
248 /*
249 * Insert it. Donate the caller's reference if RTREQFLAGS_NO_WAIT is set,
250 * otherwise retain another reference for the queue.
251 */
252 pReq->uSubmitNanoTs = RTTimeNanoTS();
253 pReq->enmState = RTREQSTATE_QUEUED;
254 unsigned fFlags = ((RTREQ volatile *)pReq)->fFlags; /* volatile paranoia */
255 if (!(fFlags & RTREQFLAGS_NO_WAIT))
256 RTReqRetain(pReq);
257
258 if (!pReq->fPoolOrQueue)
259 rtReqQueueSubmit(pReq->uOwner.hQueue, pReq);
260 else
261 rtReqPoolSubmit(pReq->uOwner.hPool, pReq);
262
263 /*
264 * Wait and return.
265 */
266 int rc = VINF_SUCCESS;
267 if (!(fFlags & RTREQFLAGS_NO_WAIT))
268 rc = RTReqWait(pReq, cMillies);
269
270 LogFlow(("RTReqSubmit: returns %Rrc\n", rc));
271 return rc;
272}
273RT_EXPORT_SYMBOL(RTReqSubmit);
274
275
276RTDECL(int) RTReqWait(PRTREQ hReq, RTMSINTERVAL cMillies)
277{
278 LogFlow(("RTReqWait: hReq=%p cMillies=%d\n", hReq, cMillies));
279
280 /*
281 * Verify the supplied package.
282 */
283 PRTREQINT pReq = hReq;
284 AssertPtrReturn(pReq, VERR_INVALID_HANDLE);
285 AssertReturn(pReq->u32Magic == RTREQ_MAGIC, VERR_INVALID_HANDLE);
286 AssertMsgReturn( pReq->enmState == RTREQSTATE_QUEUED
287 || pReq->enmState == RTREQSTATE_PROCESSING
288 || pReq->enmState == RTREQSTATE_COMPLETED,
289 ("Invalid state %d\n", pReq->enmState),
290 VERR_RT_REQUEST_STATE);
291 AssertMsgReturn(pReq->uOwner.hQueue && pReq->EventSem != NIL_RTSEMEVENT,
292 ("Invalid request package! Anyone cooking their own packages???\n"),
293 VERR_RT_REQUEST_INVALID_PACKAGE);
294 AssertMsgReturn(pReq->enmType > RTREQTYPE_INVALID && pReq->enmType < RTREQTYPE_MAX,
295 ("Invalid package type %d valid range %d-%d inclusively. This was verified on alloc too...\n",
296 pReq->enmType, RTREQTYPE_INVALID + 1, RTREQTYPE_MAX - 1),
297 VERR_RT_REQUEST_INVALID_TYPE);
298
299 /*
300 * Wait on the package.
301 */
302 int rc;
303 if (cMillies != RT_INDEFINITE_WAIT)
304 rc = RTSemEventWait(pReq->EventSem, cMillies);
305 else
306 {
307 do
308 {
309 rc = RTSemEventWait(pReq->EventSem, RT_INDEFINITE_WAIT);
310 Assert(rc != VERR_TIMEOUT);
311 } while (pReq->enmState != RTREQSTATE_COMPLETED);
312 }
313 if (rc == VINF_SUCCESS)
314 ASMAtomicXchgSize(&pReq->fEventSemClear, true);
315 if (pReq->enmState == RTREQSTATE_COMPLETED)
316 rc = VINF_SUCCESS;
317 LogFlow(("RTReqWait: returns %Rrc\n", rc));
318 Assert(rc != VERR_INTERRUPTED);
319 Assert(pReq->cRefs >= 1);
320 return rc;
321}
322RT_EXPORT_SYMBOL(RTReqWait);
323
324
325RTDECL(int) RTReqGetStatus(PRTREQ hReq)
326{
327 PRTREQINT pReq = hReq;
328 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
329 AssertReturn(pReq->u32Magic == RTREQ_MAGIC, VERR_INVALID_POINTER);
330 return pReq->iStatusX;
331}
332RT_EXPORT_SYMBOL(RTReqGetStatus);
333
334
335
336/**
337 * Process one request.
338 *
339 * @returns IPRT status code.
340 *
341 * @param pReq Request packet to process.
342 */
343DECLHIDDEN(int) rtReqProcessOne(PRTREQINT pReq)
344{
345 LogFlow(("rtReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
346
347 /*
348 * Process the request.
349 */
350 Assert(pReq->enmState == RTREQSTATE_QUEUED);
351 pReq->enmState = RTREQSTATE_PROCESSING;
352 int rcRet = VINF_SUCCESS; /* the return code of this function. */
353 int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
354 switch (pReq->enmType)
355 {
356 /*
357 * A packed down call frame.
358 */
359 case RTREQTYPE_INTERNAL:
360 {
361 uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
362 union
363 {
364 PFNRT pfn;
365 DECLCALLBACKMEMBER(int, pfn00)(void);
366 DECLCALLBACKMEMBER(int, pfn01)(uintptr_t);
367 DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t);
368 DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t);
369 DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
370 DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
371 DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
372 DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
373 DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
374 DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
375 DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
376 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);
377 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);
378 } u;
379 u.pfn = pReq->u.Internal.pfn;
380#ifndef RT_ARCH_X86
381 switch (pReq->u.Internal.cArgs)
382 {
383 case 0: rcRet = u.pfn00(); break;
384 case 1: rcRet = u.pfn01(pauArgs[0]); break;
385 case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
386 case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
387 case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
388 case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
389 case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
390 case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
391 case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
392 case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
393 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;
394 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;
395 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;
396 default:
397 AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
398 rcRet = rcReq = VERR_INTERNAL_ERROR;
399 break;
400 }
401#else /* RT_ARCH_X86 */
402 size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
403# ifdef __GNUC__
404 __asm__ __volatile__("movl %%esp, %%edx\n\t"
405 "subl %2, %%esp\n\t"
406 "andl $0xfffffff0, %%esp\n\t"
407 "shrl $2, %2\n\t"
408 "movl %%esp, %%edi\n\t"
409 "rep movsl\n\t"
410 "movl %%edx, %%edi\n\t"
411 "call *%%eax\n\t"
412 "mov %%edi, %%esp\n\t"
413 : "=a" (rcRet),
414 "=S" (pauArgs),
415 "=c" (cbArgs)
416 : "0" (u.pfn),
417 "1" (pauArgs),
418 "2" (cbArgs)
419 : "edi", "edx");
420# else
421 __asm
422 {
423 xor edx, edx /* just mess it up. */
424 mov eax, u.pfn
425 mov ecx, cbArgs
426 shr ecx, 2
427 mov esi, pauArgs
428 mov ebx, esp
429 sub esp, cbArgs
430 and esp, 0xfffffff0
431 mov edi, esp
432 rep movsd
433 call eax
434 mov esp, ebx
435 mov rcRet, eax
436 }
437# endif
438#endif /* RT_ARCH_X86 */
439 if ((pReq->fFlags & (RTREQFLAGS_RETURN_MASK)) == RTREQFLAGS_VOID)
440 rcRet = VINF_SUCCESS;
441 rcReq = rcRet;
442 break;
443 }
444
445 default:
446 AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
447 rcReq = VERR_NOT_IMPLEMENTED;
448 break;
449 }
450
451 /*
452 * Complete the request and then release our request handle reference.
453 */
454 pReq->iStatusX = rcReq;
455 pReq->enmState = RTREQSTATE_COMPLETED;
456 if (pReq->fFlags & RTREQFLAGS_NO_WAIT)
457 LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc (no wait)\n",
458 pReq, rcReq, rcRet));
459 else
460 {
461 /* Notify the waiting thread. */
462 LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - notifying waiting thread\n",
463 pReq, rcReq, rcRet));
464 ASMAtomicXchgSize(&pReq->fEventSemClear, false);
465 int rc2 = RTSemEventSignal(pReq->EventSem);
466 if (rc2 != VINF_SUCCESS)
467 {
468 AssertRC(rc2);
469 rcRet = rc2;
470 }
471 }
472 RTReqRelease(pReq);
473 return rcRet;
474}
475
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