VirtualBox

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

Last change on this file since 87182 was 85121, checked in by vboxsync, 4 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

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