VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/ioqueue/ioqueue-stdfile-provider.cpp@ 82477

Last change on this file since 82477 was 82383, checked in by vboxsync, 5 years ago

Runtime/ioqueue: Finish the fallback file provider

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.4 KB
Line 
1/* $Id: ioqueue-stdfile-provider.cpp 82383 2019-12-04 13:19:31Z vboxsync $ */
2/** @file
3 * IPRT - I/O queue, Standard file provider.
4 */
5
6/*
7 * Copyright (C) 2019 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#define LOG_GROUP RTLOGGROUP_IOQUEUE
32#include <iprt/ioqueue.h>
33
34#include <iprt/asm.h>
35#include <iprt/errcore.h>
36#include <iprt/file.h>
37#include <iprt/log.h>
38#include <iprt/mem.h>
39#include <iprt/semaphore.h>
40#include <iprt/string.h>
41#include <iprt/thread.h>
42
43#include "internal/ioqueue.h"
44
45
46/*********************************************************************************************************************************
47* Defined Constants And Macros *
48*********************************************************************************************************************************/
49
50/** The I/O queue worker thread needs to wake up the waiting thread when requests completed. */
51#define RTIOQUEUE_STDFILE_PROV_STATE_F_EVTWAIT_NEED_WAKEUP RT_BIT(0)
52/** The waiting thread was interrupted by the external wakeup call. */
53#define RTIOQUEUE_STDFILE_PROV_STATE_F_EVTWAIT_INTR RT_BIT(1)
54#define RTIOQUEUE_STDFILE_PROV_STATE_F_EVTWAIT_INTR_BIT 1
55/** The I/O queue worker thread needs to be woken up to process new requests. */
56#define RTIOQUEUE_STDFILE_PROV_STATE_F_WORKER_NEED_WAKEUP RT_BIT(2)
57#define RTIOQUEUE_STDFILE_PROV_STATE_F_WORKER_NEED_WAKEUP_BIT 2
58
59
60/*********************************************************************************************************************************
61* Structures and Typedefs *
62*********************************************************************************************************************************/
63
64
65/**
66 * Submission queue entry.
67 */
68typedef struct RTIOQUEUESSQENTRY
69{
70 /** The file to work on. */
71 RTFILE hFile;
72 /** I/O operation. */
73 RTIOQUEUEOP enmOp;
74 /** Start offset. */
75 uint64_t off;
76 /** Additional request flags. */
77 uint32_t fReqFlags;
78 /** Size of the request. */
79 size_t cbReq;
80 /** Opaque user data passed on completion. */
81 void *pvUser;
82 /** Flag whether this is a S/G or standard request. */
83 bool fSg;
84 /** Type dependent data. */
85 union
86 {
87 /** Pointer to buffer for non S/G requests. */
88 void *pvBuf;
89 /** Pointer to S/G buffer. */
90 PCRTSGBUF pSgBuf;
91 } u;
92} RTIOQUEUESSQENTRY;
93/** Pointer to a submission queue entry. */
94typedef RTIOQUEUESSQENTRY *PRTIOQUEUESSQENTRY;
95/** Pointer to a constant submission queue entry. */
96typedef const RTIOQUEUESSQENTRY *PCRTIOQUEUESSQENTRY;
97
98
99/**
100 * Internal I/O queue provider instance data.
101 */
102typedef struct RTIOQUEUEPROVINT
103{
104 /** Size of the submission queue in entries. */
105 uint32_t cSqEntries;
106 /** Size of the completion queue in entries. */
107 uint32_t cCqEntries;
108 /** Pointer to the submission queue base. */
109 PRTIOQUEUESSQENTRY paSqEntryBase;
110 /** Submission queue producer index. */
111 volatile uint32_t idxSqProd;
112 /** Submission queue producer value for any uncommitted requests. */
113 uint32_t idxSqProdUncommit;
114 /** Submission queue consumer index. */
115 volatile uint32_t idxSqCons;
116 /** Pointer to the completion queue base. */
117 PRTIOQUEUECEVT paCqEntryBase;
118 /** Completion queue producer index. */
119 volatile uint32_t idxCqProd;
120 /** Completion queue consumer index. */
121 volatile uint32_t idxCqCons;
122 /** Various state flags for synchronizing the worker thread with other participants. */
123 volatile uint32_t fState;
124 /** The worker thread handle. */
125 RTTHREAD hThrdWork;
126 /** Event semaphore the worker thread waits on for work. */
127 RTSEMEVENT hSemEvtWorker;
128 /** Event semaphore the caller waits for completion events. */
129 RTSEMEVENT hSemEvtWaitEvts;
130 /** Flag whether to shutdown the worker thread. */
131 volatile bool fShutdown;
132} RTIOQUEUEPROVINT;
133/** Pointer to the internal I/O queue provider instance data. */
134typedef RTIOQUEUEPROVINT *PRTIOQUEUEPROVINT;
135
136
137/*********************************************************************************************************************************
138* Internal Functions *
139*********************************************************************************************************************************/
140
141
142/**
143 * Processes the given submission queue entry and reports back the result in the completion queue.
144 *
145 * @returns nothing.
146 * @param pSqEntry The submission queue entry to process.
147 * @param pCqEntry The comppletion queue entry to store the result in.
148 */
149static void rtIoQueueStdFileProv_SqEntryProcess(PCRTIOQUEUESSQENTRY pSqEntry, PRTIOQUEUECEVT pCqEntry)
150{
151 int rcReq = VINF_SUCCESS;
152
153 switch (pSqEntry->enmOp)
154 {
155 case RTIOQUEUEOP_READ:
156 if (!pSqEntry->fSg)
157 rcReq = RTFileReadAt(pSqEntry->hFile, pSqEntry->off, pSqEntry->u.pvBuf, pSqEntry->cbReq, NULL);
158 else
159 {
160 RTSGBUF SgBuf;
161 RTSgBufClone(&SgBuf, pSqEntry->u.pSgBuf);
162 rcReq = RTFileSgReadAt(pSqEntry->hFile, pSqEntry->off, &SgBuf, pSqEntry->cbReq, NULL);
163 }
164 break;
165 case RTIOQUEUEOP_WRITE:
166 if (!pSqEntry->fSg)
167 rcReq = RTFileWriteAt(pSqEntry->hFile, pSqEntry->off, pSqEntry->u.pvBuf, pSqEntry->cbReq, NULL);
168 else
169 {
170 RTSGBUF SgBuf;
171 RTSgBufClone(&SgBuf, pSqEntry->u.pSgBuf);
172 rcReq = RTFileSgWriteAt(pSqEntry->hFile, pSqEntry->off, &SgBuf, pSqEntry->cbReq, NULL);
173 }
174 break;
175 case RTIOQUEUEOP_SYNC:
176 rcReq = RTFileFlush(pSqEntry->hFile);
177 break;
178 default:
179 AssertMsgFailedReturnVoid(("Invalid I/O queue operation: %d\n", pSqEntry->enmOp));
180 }
181
182 /* Write the result back into the completion queue. */
183 pCqEntry->rcReq = rcReq;
184 pCqEntry->pvUser = pSqEntry->pvUser;
185 pCqEntry->cbXfered = RT_SUCCESS(rcReq) ? pSqEntry->cbReq : 0;
186}
187
188
189/**
190 * The main I/O queue worker loop which processes the incoming I/O requests.
191 */
192static DECLCALLBACK(int) rtIoQueueStdFileProv_WorkerLoop(RTTHREAD hThrdSelf, void *pvUser)
193{
194 PRTIOQUEUEPROVINT pThis = (PRTIOQUEUEPROVINT)pvUser;
195
196 /* Signal that we started up. */
197 int rc = RTThreadUserSignal(hThrdSelf);
198 AssertRC(rc);
199
200 while (!ASMAtomicReadBool(&pThis->fShutdown))
201 {
202 /* Wait for some work. */
203 ASMAtomicOrU32(&pThis->fState, RTIOQUEUE_STDFILE_PROV_STATE_F_WORKER_NEED_WAKEUP);
204 uint32_t idxSqProd = ASMAtomicReadU32(&pThis->idxSqProd);
205 uint32_t idxSqCons = ASMAtomicReadU32(&pThis->idxSqCons);
206 uint32_t idxCqCons = ASMAtomicReadU32(&pThis->idxCqCons);
207
208 if (idxSqCons == idxSqProd)
209 {
210 rc = RTSemEventWait(pThis->hSemEvtWorker, RT_INDEFINITE_WAIT);
211 AssertRC(rc);
212
213 idxSqProd = ASMAtomicReadU32(&pThis->idxSqProd);
214 idxSqCons = ASMAtomicReadU32(&pThis->idxSqCons);
215 idxCqCons = ASMAtomicReadU32(&pThis->idxCqCons);
216 }
217
218 ASMAtomicBitTestAndClear(&pThis->fState, RTIOQUEUE_STDFILE_PROV_STATE_F_WORKER_NEED_WAKEUP_BIT);
219
220 /* Process all requests. */
221 uint32_t cCqFree = 0;
222 if (idxCqCons > pThis->idxCqProd)
223 cCqFree = pThis->cCqEntries - (pThis->cCqEntries - idxCqCons) - pThis->idxCqProd;
224 else
225 cCqFree = pThis->cCqEntries - pThis->idxCqProd - idxCqCons;
226 do
227 {
228 while ( idxSqCons != idxSqProd
229 && cCqFree)
230 {
231 PCRTIOQUEUESSQENTRY pSqEntry = &pThis->paSqEntryBase[idxSqCons];
232 PRTIOQUEUECEVT pCqEntry = &pThis->paCqEntryBase[pThis->idxCqProd];
233
234 rtIoQueueStdFileProv_SqEntryProcess(pSqEntry, pCqEntry);
235 ASMWriteFence();
236
237 idxSqCons = (idxSqCons + 1) % pThis->cSqEntries;
238 cCqFree--;
239 pThis->idxCqProd = (pThis->idxCqProd + 1) % pThis->cCqEntries;
240 ASMAtomicWriteU32(&pThis->idxSqCons, idxSqCons);
241 ASMWriteFence();
242 if (ASMAtomicReadU32(&pThis->fState) & RTIOQUEUE_STDFILE_PROV_STATE_F_EVTWAIT_NEED_WAKEUP)
243 {
244 rc = RTSemEventSignal(pThis->hSemEvtWaitEvts);
245 AssertRC(rc);
246 }
247 }
248
249 idxSqProd = ASMAtomicReadU32(&pThis->idxSqProd);
250 } while ( idxSqCons != idxSqProd
251 && cCqFree);
252 }
253
254 return VINF_SUCCESS;
255}
256
257
258/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnIsSupported} */
259static DECLCALLBACK(bool) rtIoQueueStdFileProv_IsSupported(void)
260{
261 /* The common code/public API already checked for the proper handle type. */
262 return true;
263}
264
265
266/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnQueueInit} */
267static DECLCALLBACK(int) rtIoQueueStdFileProv_QueueInit(RTIOQUEUEPROV hIoQueueProv, uint32_t fFlags,
268 uint32_t cSqEntries, uint32_t cCqEntries)
269{
270 RT_NOREF(fFlags);
271
272 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
273 int rc = VINF_SUCCESS;
274
275 cSqEntries++;
276 cCqEntries++;
277
278 pThis->cSqEntries = cSqEntries;
279 pThis->cCqEntries = cCqEntries;
280 pThis->idxSqProd = 0;
281 pThis->idxSqProdUncommit = 0;
282 pThis->idxSqCons = 0;
283 pThis->idxCqProd = 0;
284 pThis->idxCqCons = 0;
285 pThis->fShutdown = false;
286 pThis->fState = 0;
287
288 pThis->paSqEntryBase = (PRTIOQUEUESSQENTRY)RTMemAllocZ(cSqEntries * sizeof(RTIOQUEUESSQENTRY));
289 if (RT_LIKELY(pThis->paSqEntryBase))
290 {
291 pThis->paCqEntryBase = (PRTIOQUEUECEVT)RTMemAllocZ(cCqEntries * sizeof(RTIOQUEUECEVT));
292 if (RT_LIKELY(pThis->paSqEntryBase))
293 {
294 rc = RTSemEventCreate(&pThis->hSemEvtWorker);
295 if (RT_SUCCESS(rc))
296 {
297 rc = RTSemEventCreate(&pThis->hSemEvtWaitEvts);
298 if (RT_SUCCESS(rc))
299 {
300 /* Spin up the worker thread. */
301 rc = RTThreadCreate(&pThis->hThrdWork, rtIoQueueStdFileProv_WorkerLoop, pThis, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
302 "IoQ-StdFile");
303 if (RT_SUCCESS(rc))
304 {
305 rc = RTThreadUserWait(pThis->hThrdWork, 10 * RT_MS_1SEC);
306 AssertRC(rc);
307
308 return VINF_SUCCESS;
309 }
310
311 RTSemEventDestroy(pThis->hSemEvtWaitEvts);
312 }
313
314 RTSemEventDestroy(pThis->hSemEvtWorker);
315 }
316
317 RTMemFree(pThis->paCqEntryBase);
318 }
319 else
320 rc = VERR_NO_MEMORY;
321
322 RTMemFree(pThis->paSqEntryBase);
323 }
324 else
325 rc = VERR_NO_MEMORY;
326
327 return rc;
328}
329
330
331/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnQueueDestroy} */
332static DECLCALLBACK(void) rtIoQueueStdFileProv_QueueDestroy(RTIOQUEUEPROV hIoQueueProv)
333{
334 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
335
336 ASMAtomicXchgBool(&pThis->fShutdown, true);
337 RTSemEventSignal(pThis->hSemEvtWorker);
338
339 int rc = RTThreadWait(pThis->hThrdWork, 60 * RT_MS_1SEC, NULL);
340 AssertRC(rc);
341
342 RTSemEventDestroy(pThis->hSemEvtWaitEvts);
343 RTSemEventDestroy(pThis->hSemEvtWorker);
344 RTMemFree(pThis->paCqEntryBase);
345 RTMemFree(pThis->paSqEntryBase);
346 RT_BZERO(pThis, sizeof(*pThis));
347}
348
349
350/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnHandleRegister} */
351static DECLCALLBACK(int) rtIoQueueStdFileProv_HandleRegister(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle)
352{
353 RT_NOREF(hIoQueueProv, pHandle);
354
355 /* Nothing to do here. */
356 return VINF_SUCCESS;
357}
358
359
360/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnHandleDeregister} */
361static DECLCALLBACK(int) rtIoQueueStdFileProv_HandleDeregister(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle)
362{
363 RT_NOREF(hIoQueueProv, pHandle);
364
365 /* Nothing to do here. */
366 return VINF_SUCCESS;
367}
368
369
370/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnReqPrepare} */
371static DECLCALLBACK(int) rtIoQueueStdFileProv_ReqPrepare(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle, RTIOQUEUEOP enmOp,
372 uint64_t off, void *pvBuf, size_t cbBuf, uint32_t fReqFlags,
373 void *pvUser)
374{
375 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
376 PRTIOQUEUESSQENTRY pSqEntry = &pThis->paSqEntryBase[pThis->idxSqProdUncommit];
377
378 pSqEntry->hFile = pHandle->u.hFile;
379 pSqEntry->enmOp = enmOp;
380 pSqEntry->off = off;
381 pSqEntry->fReqFlags = fReqFlags;
382 pSqEntry->cbReq = cbBuf;
383 pSqEntry->pvUser = pvUser;
384 pSqEntry->fSg = false;
385 pSqEntry->u.pvBuf = pvBuf;
386
387 pThis->idxSqProdUncommit = (pThis->idxSqProdUncommit + 1) % pThis->cSqEntries;
388 return VINF_SUCCESS;
389}
390
391
392/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnReqPrepareSg} */
393static DECLCALLBACK(int) rtIoQueueStdFileProv_ReqPrepareSg(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle, RTIOQUEUEOP enmOp,
394 uint64_t off, PCRTSGBUF pSgBuf, size_t cbSg, uint32_t fReqFlags,
395 void *pvUser)
396{
397 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
398 PRTIOQUEUESSQENTRY pSqEntry = &pThis->paSqEntryBase[pThis->idxSqProdUncommit];
399
400 pSqEntry->hFile = pHandle->u.hFile;
401 pSqEntry->enmOp = enmOp;
402 pSqEntry->off = off;
403 pSqEntry->fReqFlags = fReqFlags;
404 pSqEntry->cbReq = cbSg;
405 pSqEntry->pvUser = pvUser;
406 pSqEntry->fSg = true;
407 pSqEntry->u.pSgBuf = pSgBuf;
408
409 pThis->idxSqProdUncommit = (pThis->idxSqProdUncommit + 1) % pThis->cSqEntries;
410 return VINF_SUCCESS;
411}
412
413
414/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnCommit} */
415static DECLCALLBACK(int) rtIoQueueStdFileProv_Commit(RTIOQUEUEPROV hIoQueueProv, uint32_t *pcReqsCommitted)
416{
417 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
418
419 if (pThis->idxSqProd > pThis->idxSqProdUncommit)
420 *pcReqsCommitted = pThis->cSqEntries - pThis->idxSqProd + pThis->idxSqProdUncommit;
421 else
422 *pcReqsCommitted = pThis->idxSqProdUncommit - pThis->idxSqProd;
423
424 ASMWriteFence();
425 ASMAtomicWriteU32(&pThis->idxSqProd, pThis->idxSqProdUncommit);
426 return RTSemEventSignal(pThis->hSemEvtWorker);
427}
428
429
430/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnEvtWait} */
431static DECLCALLBACK(int) rtIoQueueStdFileProv_EvtWait(RTIOQUEUEPROV hIoQueueProv, PRTIOQUEUECEVT paCEvt, uint32_t cCEvt,
432 uint32_t cMinWait, uint32_t *pcCEvt, uint32_t fFlags)
433{
434 RT_NOREF(fFlags);
435
436 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
437 int rc = VINF_SUCCESS;
438 uint32_t idxCEvt = 0;
439
440 while ( RT_SUCCESS(rc)
441 && cMinWait
442 && cCEvt)
443 {
444 ASMAtomicOrU32(&pThis->fState, RTIOQUEUE_STDFILE_PROV_STATE_F_EVTWAIT_NEED_WAKEUP);
445 uint32_t idxCqProd = ASMAtomicReadU32(&pThis->idxCqProd);
446 uint32_t idxCqCons = ASMAtomicReadU32(&pThis->idxCqCons);
447
448 if (idxCqCons == idxCqProd)
449 {
450 rc = RTSemEventWait(pThis->hSemEvtWaitEvts, RT_INDEFINITE_WAIT);
451 AssertRC(rc);
452 if (ASMAtomicBitTestAndClear(&pThis->fState, RTIOQUEUE_STDFILE_PROV_STATE_F_EVTWAIT_INTR_BIT))
453 {
454 rc = VERR_INTERRUPTED;
455 ASMAtomicBitTestAndClear(&pThis->fState, RTIOQUEUE_STDFILE_PROV_STATE_F_WORKER_NEED_WAKEUP_BIT);
456 break;
457 }
458
459 idxCqProd = ASMAtomicReadU32(&pThis->idxCqProd);
460 idxCqCons = ASMAtomicReadU32(&pThis->idxCqCons);
461 }
462
463 ASMAtomicBitTestAndClear(&pThis->fState, RTIOQUEUE_STDFILE_PROV_STATE_F_WORKER_NEED_WAKEUP_BIT);
464
465 /* Process all requests. */
466 while ( idxCqCons != idxCqProd
467 && cCEvt)
468 {
469 PRTIOQUEUECEVT pCqEntry = &pThis->paCqEntryBase[idxCqCons];
470
471 paCEvt[idxCEvt].rcReq = pCqEntry->rcReq;
472 paCEvt[idxCEvt].pvUser = pCqEntry->pvUser;
473 paCEvt[idxCEvt].cbXfered = pCqEntry->cbXfered;
474 ASMReadFence();
475
476 idxCEvt++;
477 cCEvt--;
478 cMinWait--;
479
480 idxCqCons = (idxCqCons + 1) % pThis->cCqEntries;
481 pThis->idxCqCons = (pThis->idxCqCons + 1) % pThis->cCqEntries;
482 ASMWriteFence();
483 }
484 }
485
486 *pcCEvt = idxCEvt;
487 return rc;
488}
489
490
491/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnEvtWaitWakeup} */
492static DECLCALLBACK(int) rtIoQueueStdFileProv_EvtWaitWakeup(RTIOQUEUEPROV hIoQueueProv)
493{
494 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
495
496 ASMAtomicOrU32(&pThis->fState, RTIOQUEUE_STDFILE_PROV_STATE_F_EVTWAIT_INTR);
497 return RTSemEventSignal(pThis->hSemEvtWaitEvts);
498}
499
500
501/**
502 * Standard file I/O queue provider virtual method table.
503 */
504RT_DECL_DATA_CONST(RTIOQUEUEPROVVTABLE const) g_RTIoQueueStdFileProv =
505{
506 /** uVersion */
507 RTIOQUEUEPROVVTABLE_VERSION,
508 /** pszId */
509 "StdFile",
510 /** cbIoQueueProv */
511 sizeof(RTIOQUEUEPROVINT),
512 /** enmHnd */
513 RTHANDLETYPE_FILE,
514 /** fFlags */
515 0,
516 /** pfnIsSupported */
517 rtIoQueueStdFileProv_IsSupported,
518 /** pfnQueueInit */
519 rtIoQueueStdFileProv_QueueInit,
520 /** pfnQueueDestroy */
521 rtIoQueueStdFileProv_QueueDestroy,
522 /** pfnHandleRegister */
523 rtIoQueueStdFileProv_HandleRegister,
524 /** pfnHandleDeregister */
525 rtIoQueueStdFileProv_HandleDeregister,
526 /** pfnReqPrepare */
527 rtIoQueueStdFileProv_ReqPrepare,
528 /** pfnReqPrepareSg */
529 rtIoQueueStdFileProv_ReqPrepareSg,
530 /** pfnCommit */
531 rtIoQueueStdFileProv_Commit,
532 /** pfnEvtWait */
533 rtIoQueueStdFileProv_EvtWait,
534 /** pfnEvtWaitWakeup */
535 rtIoQueueStdFileProv_EvtWaitWakeup,
536 /** uEndMarker */
537 RTIOQUEUEPROVVTABLE_VERSION
538};
539
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