VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 19.8 KB
Line 
1/* $Id: req.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Request packets
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/req.h>
42#include "internal/iprt.h"
43
44#include <iprt/assert.h>
45#include <iprt/asm.h>
46#include <iprt/err.h>
47#include <iprt/string.h>
48#include <iprt/time.h>
49#include <iprt/semaphore.h>
50#include <iprt/thread.h>
51#include <iprt/log.h>
52#include <iprt/mem.h>
53
54#include "internal/req.h"
55#include "internal/magics.h"
56
57
58/*********************************************************************************************************************************
59* Internal Functions *
60*********************************************************************************************************************************/
61
62
63/**
64 * Allocate a new request from the heap.
65 *
66 * @returns IPRT status code.
67 * @param enmType The reques type.
68 * @param fPoolOrQueue The owner type.
69 * @param pvOwner The owner.
70 * @param phReq Where to return the request handle.
71 */
72DECLHIDDEN(int) rtReqAlloc(RTREQTYPE enmType, bool fPoolOrQueue, void *pvOwner, PRTREQ *phReq)
73{
74 PRTREQ pReq = (PRTREQ)RTMemAllocZ(sizeof(*pReq));
75 if (RT_UNLIKELY(!pReq))
76 return VERR_NO_MEMORY;
77
78 /*
79 * Create the semaphore used for waiting.
80 */
81 int rc = RTSemEventCreate(&pReq->EventSem);
82 AssertRCReturnStmt(rc, RTMemFree(pReq), rc);
83
84 /*
85 * Initialize the packet and return it.
86 */
87 pReq->u32Magic = RTREQ_MAGIC;
88 pReq->fEventSemClear = true;
89 pReq->fSignalPushBack = true;
90 pReq->fPoolOrQueue = fPoolOrQueue;
91 pReq->iStatusX = VERR_RT_REQUEST_STATUS_STILL_PENDING;
92 pReq->enmState = RTREQSTATE_ALLOCATED;
93 pReq->pNext = NULL;
94 pReq->uOwner.pv = pvOwner;
95 pReq->fFlags = RTREQFLAGS_IPRT_STATUS;
96 pReq->enmType = enmType;
97 pReq->cRefs = 1;
98
99 *phReq = pReq;
100 return VINF_SUCCESS;
101}
102
103
104/**
105 * Re-initializes a request when it's being recycled.
106 *
107 * @returns IRPT status code, the request is freed on failure.
108 * @param pReq The request.
109 * @param enmType The request type.
110 */
111DECLHIDDEN(int) rtReqReInit(PRTREQINT pReq, RTREQTYPE enmType)
112{
113 Assert(pReq->u32Magic == RTREQ_MAGIC);
114 Assert(pReq->enmType == RTREQTYPE_INVALID);
115 Assert(pReq->enmState == RTREQSTATE_FREE);
116 Assert(pReq->cRefs == 0);
117
118 /*
119 * Make sure the event sem is not signaled.
120 */
121 if (!pReq->fEventSemClear)
122 {
123 int rc = RTSemEventWait(pReq->EventSem, 0);
124 if (rc != VINF_SUCCESS && rc != VERR_TIMEOUT)
125 {
126 /*
127 * This shall not happen, but if it does we'll just destroy
128 * the semaphore and create a new one.
129 */
130 AssertMsgFailed(("rc=%Rrc from RTSemEventWait(%#x).\n", rc, pReq->EventSem));
131 RTSemEventDestroy(pReq->EventSem);
132 rc = RTSemEventCreate(&pReq->EventSem);
133 if (RT_FAILURE(rc))
134 {
135 AssertRC(rc);
136 pReq->EventSem = NIL_RTSEMEVENT;
137 rtReqFreeIt(pReq);
138 return rc;
139 }
140 }
141 pReq->fEventSemClear = true;
142 }
143 else
144 Assert(RTSemEventWait(pReq->EventSem, 0) == VERR_TIMEOUT);
145
146 /*
147 * Initialize the packet and return it.
148 */
149 ASMAtomicWriteNullPtr(&pReq->pNext);
150 pReq->iStatusX = VERR_RT_REQUEST_STATUS_STILL_PENDING;
151 pReq->enmState = RTREQSTATE_ALLOCATED;
152 pReq->fFlags = RTREQFLAGS_IPRT_STATUS;
153 pReq->enmType = enmType;
154 pReq->cRefs = 1;
155 return VINF_SUCCESS;
156}
157
158
159RTDECL(uint32_t) RTReqRetain(PRTREQ hReq)
160{
161 PRTREQINT pReq = hReq;
162 AssertPtrReturn(pReq, UINT32_MAX);
163 AssertReturn(pReq->u32Magic == RTREQ_MAGIC, UINT32_MAX);
164
165 return ASMAtomicIncU32(&pReq->cRefs);
166}
167RT_EXPORT_SYMBOL(RTReqRetain);
168
169
170/**
171 * Frees a request.
172 *
173 * @param pReq The request.
174 */
175DECLHIDDEN(void) rtReqFreeIt(PRTREQINT pReq)
176{
177 Assert(pReq->u32Magic == RTREQ_MAGIC);
178 Assert(pReq->cRefs == 0);
179
180 pReq->u32Magic = RTREQ_MAGIC_DEAD;
181 RTSemEventDestroy(pReq->EventSem);
182 pReq->EventSem = NIL_RTSEMEVENT;
183 RTSemEventMultiDestroy(pReq->hPushBackEvt);
184 pReq->hPushBackEvt = NIL_RTSEMEVENTMULTI;
185 RTMemFree(pReq);
186}
187
188
189RTDECL(uint32_t) RTReqRelease(PRTREQ hReq)
190{
191 /*
192 * Ignore NULL and validate the request.
193 */
194 if (!hReq)
195 return 0;
196 PRTREQINT pReq = hReq;
197 AssertPtrReturn(pReq, UINT32_MAX);
198 AssertReturn(pReq->u32Magic == RTREQ_MAGIC, UINT32_MAX);
199
200 /*
201 * Drop a reference, recycle the request when we reach 0.
202 */
203 uint32_t cRefs = ASMAtomicDecU32(&pReq->cRefs);
204 if (cRefs == 0)
205 {
206 /*
207 * Check packet state.
208 */
209 RTREQSTATE const enmState = pReq->enmState;
210 switch (enmState)
211 {
212 case RTREQSTATE_ALLOCATED:
213 case RTREQSTATE_COMPLETED:
214 break;
215 default:
216 AssertMsgFailedReturn(("Invalid state %d!\n", enmState), 0);
217 }
218
219 /*
220 * Make it a free packet and put it into one of the free packet lists.
221 */
222 pReq->enmState = RTREQSTATE_FREE;
223 pReq->iStatusX = VERR_RT_REQUEST_STATUS_FREED;
224 pReq->enmType = RTREQTYPE_INVALID;
225
226 bool fRecycled;
227 if (pReq->fPoolOrQueue)
228 fRecycled = rtReqPoolRecycle(pReq->uOwner.hPool, pReq);
229 else
230 fRecycled = rtReqQueueRecycle(pReq->uOwner.hQueue, pReq);
231 if (!fRecycled)
232 rtReqFreeIt(pReq);
233 }
234
235 return cRefs;
236}
237RT_EXPORT_SYMBOL(RTReqRelease);
238
239
240RTDECL(int) RTReqSubmit(PRTREQ hReq, RTMSINTERVAL cMillies)
241{
242 LogFlow(("RTReqSubmit: hReq=%p cMillies=%d\n", hReq, cMillies));
243
244 /*
245 * Verify the supplied package.
246 */
247 PRTREQINT pReq = hReq;
248 AssertPtrReturn(pReq, VERR_INVALID_HANDLE);
249 AssertReturn(pReq->u32Magic == RTREQ_MAGIC, VERR_INVALID_HANDLE);
250 AssertMsgReturn(pReq->enmState == RTREQSTATE_ALLOCATED, ("%d\n", pReq->enmState), VERR_RT_REQUEST_STATE);
251 AssertMsgReturn(pReq->uOwner.hQueue && !pReq->pNext && pReq->EventSem != NIL_RTSEMEVENT,
252 ("Invalid request package! Anyone cooking their own packages???\n"),
253 VERR_RT_REQUEST_INVALID_PACKAGE);
254 AssertMsgReturn(pReq->enmType > RTREQTYPE_INVALID && pReq->enmType < RTREQTYPE_MAX,
255 ("Invalid package type %d valid range %d-%d inclusively. This was verified on alloc too...\n",
256 pReq->enmType, RTREQTYPE_INVALID + 1, RTREQTYPE_MAX - 1),
257 VERR_RT_REQUEST_INVALID_TYPE);
258
259 /*
260 * Insert it. Always grab a reference for the queue (we used to
261 * donate the caller's reference in the NO_WAIT case once upon a time).
262 */
263 pReq->uSubmitNanoTs = RTTimeNanoTS();
264 pReq->enmState = RTREQSTATE_QUEUED;
265 unsigned fFlags = ((RTREQ volatile *)pReq)->fFlags; /* volatile paranoia */
266 RTReqRetain(pReq);
267
268 if (!pReq->fPoolOrQueue)
269 rtReqQueueSubmit(pReq->uOwner.hQueue, pReq);
270 else
271 rtReqPoolSubmit(pReq->uOwner.hPool, pReq);
272
273 /*
274 * Wait and return.
275 */
276 int rc = VINF_SUCCESS;
277 if (!(fFlags & RTREQFLAGS_NO_WAIT))
278 rc = RTReqWait(pReq, cMillies);
279
280 LogFlow(("RTReqSubmit: returns %Rrc\n", rc));
281 return rc;
282}
283RT_EXPORT_SYMBOL(RTReqSubmit);
284
285
286RTDECL(int) RTReqWait(PRTREQ hReq, RTMSINTERVAL cMillies)
287{
288 LogFlow(("RTReqWait: hReq=%p cMillies=%d\n", hReq, cMillies));
289
290 /*
291 * Verify the supplied package.
292 */
293 PRTREQINT pReq = hReq;
294 AssertPtrReturn(pReq, VERR_INVALID_HANDLE);
295 AssertReturn(pReq->u32Magic == RTREQ_MAGIC, VERR_INVALID_HANDLE);
296 RTREQSTATE enmState = pReq->enmState;
297 AssertMsgReturn( enmState == RTREQSTATE_QUEUED
298 || enmState == RTREQSTATE_PROCESSING
299 || enmState == RTREQSTATE_COMPLETED
300 || enmState == RTREQSTATE_CANCELLED,
301 ("Invalid state %d\n", enmState),
302 VERR_RT_REQUEST_STATE);
303 AssertMsgReturn(pReq->uOwner.hQueue && pReq->EventSem != NIL_RTSEMEVENT,
304 ("Invalid request package! Anyone cooking their own packages???\n"),
305 VERR_RT_REQUEST_INVALID_PACKAGE);
306 AssertMsgReturn(pReq->enmType > RTREQTYPE_INVALID && pReq->enmType < RTREQTYPE_MAX,
307 ("Invalid package type %d valid range %d-%d inclusively. This was verified on alloc too...\n",
308 pReq->enmType, RTREQTYPE_INVALID + 1, RTREQTYPE_MAX - 1),
309 VERR_RT_REQUEST_INVALID_TYPE);
310
311 /*
312 * Wait on the package.
313 */
314 int rc;
315 if (cMillies != RT_INDEFINITE_WAIT)
316 rc = RTSemEventWait(pReq->EventSem, cMillies);
317 else
318 {
319 do
320 {
321 rc = RTSemEventWait(pReq->EventSem, RT_INDEFINITE_WAIT);
322 Assert(rc != VERR_TIMEOUT);
323 } while (pReq->enmState != RTREQSTATE_COMPLETED);
324 }
325 if (rc == VINF_SUCCESS)
326 ASMAtomicWriteBool(&pReq->fEventSemClear, true);
327 if (pReq->enmState == RTREQSTATE_COMPLETED)
328 rc = VINF_SUCCESS;
329 LogFlow(("RTReqWait: returns %Rrc\n", rc));
330 Assert(rc != VERR_INTERRUPTED);
331 Assert(pReq->cRefs >= 1);
332 return rc;
333}
334RT_EXPORT_SYMBOL(RTReqWait);
335
336
337RTDECL(int) RTReqCancel(PRTREQ hReq)
338{
339 LogFlow(("RTReqCancel: hReq=%p\n", hReq));
340
341 /*
342 * Verify the supplied package.
343 */
344 PRTREQINT pReq = hReq;
345 AssertPtrReturn(pReq, VERR_INVALID_HANDLE);
346 AssertReturn(pReq->u32Magic == RTREQ_MAGIC, VERR_INVALID_HANDLE);
347 AssertMsgReturn(pReq->uOwner.hQueue && pReq->EventSem != NIL_RTSEMEVENT,
348 ("Invalid request package! Anyone cooking their own packages???\n"),
349 VERR_RT_REQUEST_INVALID_PACKAGE);
350 AssertMsgReturn(pReq->enmType > RTREQTYPE_INVALID && pReq->enmType < RTREQTYPE_MAX,
351 ("Invalid package type %d valid range %d-%d inclusively. This was verified on alloc too...\n",
352 pReq->enmType, RTREQTYPE_INVALID + 1, RTREQTYPE_MAX - 1),
353 VERR_RT_REQUEST_INVALID_TYPE);
354
355 /*
356 * Try cancel the request itself by changing its state.
357 */
358 int rc;
359 if (ASMAtomicCmpXchgU32((uint32_t volatile *)&pReq->enmState, RTREQSTATE_CANCELLED, RTREQSTATE_QUEUED))
360 {
361 if (pReq->fPoolOrQueue)
362 rtReqPoolCancel(pReq->uOwner.hPool, pReq);
363 rc = VINF_SUCCESS;
364 }
365 else
366 {
367 Assert(pReq->enmState == RTREQSTATE_PROCESSING || pReq->enmState == RTREQSTATE_COMPLETED);
368 rc = VERR_RT_REQUEST_STATE;
369 }
370
371 LogFlow(("RTReqCancel: returns %Rrc\n", rc));
372 return rc;
373}
374RT_EXPORT_SYMBOL(RTReqCancel);
375
376
377RTDECL(int) RTReqGetStatus(PRTREQ hReq)
378{
379 PRTREQINT pReq = hReq;
380 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
381 AssertReturn(pReq->u32Magic == RTREQ_MAGIC, VERR_INVALID_POINTER);
382 return pReq->iStatusX;
383}
384RT_EXPORT_SYMBOL(RTReqGetStatus);
385
386
387
388/**
389 * Process one request.
390 *
391 * @returns IPRT status code.
392 *
393 * @param pReq Request packet to process.
394 */
395DECLHIDDEN(int) rtReqProcessOne(PRTREQINT pReq)
396{
397 LogFlow(("rtReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
398
399 /*
400 * Try switch the request status to processing.
401 */
402 int rcRet = VINF_SUCCESS; /* the return code of this function. */
403 int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
404 if (ASMAtomicCmpXchgU32((uint32_t volatile *)&pReq->enmState, RTREQSTATE_PROCESSING, RTREQSTATE_QUEUED))
405 {
406 /*
407 * Process the request.
408 */
409 pReq->enmState = RTREQSTATE_PROCESSING;
410 switch (pReq->enmType)
411 {
412 /*
413 * A packed down call frame.
414 */
415 case RTREQTYPE_INTERNAL:
416 {
417 uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
418 union
419 {
420 PFNRT pfn;
421 DECLCALLBACKMEMBER(int, pfn00,(void));
422 DECLCALLBACKMEMBER(int, pfn01,(uintptr_t));
423 DECLCALLBACKMEMBER(int, pfn02,(uintptr_t, uintptr_t));
424 DECLCALLBACKMEMBER(int, pfn03,(uintptr_t, uintptr_t, uintptr_t));
425 DECLCALLBACKMEMBER(int, pfn04,(uintptr_t, uintptr_t, uintptr_t, uintptr_t));
426 DECLCALLBACKMEMBER(int, pfn05,(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t));
427 DECLCALLBACKMEMBER(int, pfn06,(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t));
428 DECLCALLBACKMEMBER(int, pfn07,(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t));
429 DECLCALLBACKMEMBER(int, pfn08,(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t));
430 DECLCALLBACKMEMBER(int, pfn09,(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t));
431 DECLCALLBACKMEMBER(int, pfn10,(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t));
432 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));
433 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));
434 } u;
435 u.pfn = pReq->u.Internal.pfn;
436#ifndef RT_ARCH_X86
437 switch (pReq->u.Internal.cArgs)
438 {
439 case 0: rcRet = u.pfn00(); break;
440 case 1: rcRet = u.pfn01(pauArgs[0]); break;
441 case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
442 case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
443 case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
444 case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
445 case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
446 case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
447 case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
448 case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
449 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;
450 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;
451 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;
452 default:
453 AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
454 rcRet = rcReq = VERR_INTERNAL_ERROR;
455 break;
456 }
457#else /* RT_ARCH_X86 */
458 size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
459# ifdef __GNUC__
460 __asm__ __volatile__("movl %%esp, %%edx\n\t"
461 "subl %2, %%esp\n\t"
462 "andl $0xfffffff0, %%esp\n\t"
463 "shrl $2, %2\n\t"
464 "movl %%esp, %%edi\n\t"
465 "rep movsl\n\t"
466 "movl %%edx, %%edi\n\t"
467 "call *%%eax\n\t"
468 "mov %%edi, %%esp\n\t"
469 : "=a" (rcRet),
470 "=S" (pauArgs),
471 "=c" (cbArgs)
472 : "0" (u.pfn),
473 "1" (pauArgs),
474 "2" (cbArgs)
475 : "edi", "edx");
476# else
477 __asm
478 {
479 xor edx, edx /* just mess it up. */
480 mov eax, u.pfn
481 mov ecx, cbArgs
482 shr ecx, 2
483 mov esi, pauArgs
484 mov ebx, esp
485 sub esp, cbArgs
486 and esp, 0xfffffff0
487 mov edi, esp
488 rep movsd
489 call eax
490 mov esp, ebx
491 mov rcRet, eax
492 }
493# endif
494#endif /* RT_ARCH_X86 */
495 if ((pReq->fFlags & (RTREQFLAGS_RETURN_MASK)) == RTREQFLAGS_VOID)
496 rcRet = VINF_SUCCESS;
497 rcReq = rcRet;
498 break;
499 }
500
501 default:
502 AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
503 rcReq = VERR_NOT_IMPLEMENTED;
504 break;
505 }
506 }
507 else
508 {
509 Assert(pReq->enmState == RTREQSTATE_CANCELLED);
510 rcReq = VERR_CANCELLED;
511 }
512
513 /*
514 * Complete the request and then release our request handle reference.
515 */
516 pReq->iStatusX = rcReq;
517 pReq->enmState = RTREQSTATE_COMPLETED;
518 if (pReq->fFlags & RTREQFLAGS_NO_WAIT)
519 LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc (no wait)\n",
520 pReq, rcReq, rcRet));
521 else
522 {
523 /* Notify the waiting thread. */
524 LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - notifying waiting thread\n",
525 pReq, rcReq, rcRet));
526 ASMAtomicWriteBool(&pReq->fEventSemClear, false);
527 int rc2 = RTSemEventSignal(pReq->EventSem);
528 if (rc2 != VINF_SUCCESS)
529 {
530 AssertRC(rc2);
531 rcRet = rc2;
532 }
533 }
534 RTReqRelease(pReq);
535 return rcRet;
536}
537
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use