VirtualBox

source: vbox/trunk/include/VBox/pdmasynccompletion.h@ 8746

Last change on this file since 8746 was 8746, checked in by vboxsync, 16 years ago

Updates to the async completion manager. PDMR3AsyncCompletionTaskSubmit takes an array of tasks now to minimize overhead of the wakeups

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 19.7 KB
Line 
1/* $Id: pdmasynccompletion.h 8746 2008-05-09 19:34:44Z vboxsync $ */
2/** @file
3 * PDM - Pluggable Device Manager, Async I/O Completion.
4 */
5
6/*
7 * Copyright (C) 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#ifndef ___VBox_pdmasynccompletion_h
32#define ___VBox_pdmasynccompletion_h
33
34#include <VBox/types.h>
35#include <VBox/err.h>
36#include <iprt/assert.h>
37
38__BEGIN_DECLS
39
40/** @defgroup grp_pdm_async_completion Async I/O Completion
41 * @ingroup grp_pdm
42 * @{
43 */
44
45/** Pointer to a PDM async completion template handle. */
46typedef struct PDMASYNCCOMPLETIONTEMPLATE *PPDMASYNCCOMPLETIONTEMPLATE;
47/** Pointer to a PDM async completion template handle pointer. */
48typedef PPDMASYNCCOMPLETIONTEMPLATE *PPPDMASYNCCOMPLETIONTEMPLATE;
49
50/** Pointer to a PDM async completion task handle. */
51typedef struct PDMASYNCCOMPLETIONTASK *PPDMASYNCCOMPLETIONTASK;
52/** Pointer to a PDM async completion task handle pointer. */
53typedef PPDMASYNCCOMPLETIONTASK *PPPDMASYNCCOMPLETIONTASK;
54
55
56/**
57 * Completion callback for devices.
58 *
59 * @param pDevIns The device instance.
60 * @param pTask Pointer to the completion task.
61 * The task is at the time of the call setup to be resumed. So, the callback must
62 * call PDMR3AsyncCompletionSuspend or PDMR3AsyncCompletionDestroy if any other
63 * action is wanted upon return.
64 * @param pvCtx Pointer to any additional, OS specific, completion context. TBD.
65 * @param pvUser User argument.
66 */
67typedef DECLCALLBACK(void) FNPDMASYNCCOMPLETEDEV(PPDMDEVINS pDevIns, PPDMASYNCCOMPLETIONTASK pTask, void *pvCtx, void *pvUser);
68/** Pointer to a FNPDMASYNCCOMPLETEDEV(). */
69typedef FNPDMASYNCCOMPLETEDEV *PFNPDMASYNCCOMPLETEDEV;
70
71
72/**
73 * Completion callback for drivers.
74 *
75 * @param pDrvIns The driver instance.
76 * @param pTask Pointer to the completion task.
77 * The task is at the time of the call setup to be resumed. So, the callback must
78 * call PDMR3AsyncCompletionSuspend or PDMR3AsyncCompletionDestroy if any other
79 * action is wanted upon return.
80 * @param pvCtx Pointer to any additional, OS specific, completion context. TBD.
81 * @param pvUser User argument.
82 */
83typedef DECLCALLBACK(void) FNPDMASYNCCOMPLETEDRV(PPDMDRVINS pDrvIns, PPDMASYNCCOMPLETIONTASK pTask, void *pvCtx, void *pvUser);
84/** Pointer to a FNPDMASYNCCOMPLETEDRV(). */
85typedef FNPDMASYNCCOMPLETEDRV *PFNPDMASYNCCOMPLETEDRV;
86
87
88/**
89 * Completion callback for USB devices.
90 *
91 * @param pUsbIns The USB device instance.
92 * @param pTask Pointer to the completion task.
93 * The task is at the time of the call setup to be resumed. So, the callback must
94 * call PDMR3AsyncCompletionSuspend or PDMR3AsyncCompletionDestroy if any other
95 * action is wanted upon return.
96 * @param pvCtx Pointer to any additional, OS specific, completion context. TBD.
97 * @param pvUser User argument.
98 */
99typedef DECLCALLBACK(void) FNPDMASYNCCOMPLETEUSB(PPDMUSBINS pUsbIns, PPDMASYNCCOMPLETIONTASK pTask, void *pvCtx, void *pvUser);
100/** Pointer to a FNPDMASYNCCOMPLETEUSB(). */
101typedef FNPDMASYNCCOMPLETEUSB *PFNPDMASYNCCOMPLETEUSB;
102
103
104/**
105 * Completion callback for internal.
106 *
107 * @param pVM Pointer to the shared VM structure.
108 * @param pTask Pointer to the completion task.
109 * The task is at the time of the call setup to be resumed. So, the callback must
110 * call PDMR3AsyncCompletionSuspend or PDMR3AsyncCompletionDestroy if any other
111 * action is wanted upon return.
112 * @param pvCtx Pointer to any additional, OS specific, completion context. TBD.
113 * @param pvUser User argument for the task.
114 * @param pvUser2 User argument for the template.
115 */
116typedef DECLCALLBACK(void) FNPDMASYNCCOMPLETEINT(PVM pVM, PPDMASYNCCOMPLETIONTASK pTask, void *pvCtx, void *pvUser, void *pvUser2);
117/** Pointer to a FNPDMASYNCCOMPLETEINT(). */
118typedef FNPDMASYNCCOMPLETEINT *PFNPDMASYNCCOMPLETEINT;
119
120
121/**
122 * Creates a async completion template for a device instance.
123 *
124 * The template is used when creating new completion tasks.
125 *
126 * @returns VBox status code.
127 * @param pVM Pointer to the shared VM structure.
128 * @param pDevIns The device instance.
129 * @param ppTemplate Where to store the template pointer on success.
130 * @param pfnCompleted The completion callback routine.
131 * @param pszDesc Description.
132 */
133PDMR3DECL(int) PDMR3AsyncCompletionTemplateCreateDevice(PVM pVM, PPDMDEVINS pDevIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEDEV pfnCompleted, const char *pszDesc);
134
135/**
136 * Creates a async completion template for a driver instance.
137 *
138 * The template is used when creating new completion tasks.
139 *
140 * @returns VBox status code.
141 * @param pVM Pointer to the shared VM structure.
142 * @param pDrvIns The driver instance.
143 * @param ppTemplate Where to store the template pointer on success.
144 * @param pfnCompleted The completion callback routine.
145 * @param pszDesc Description.
146 */
147PDMR3DECL(int) PDMR3AsyncCompletionTemplateCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEDRV pfnCompleted, const char *pszDesc);
148
149/**
150 * Creates a async completion template for a USB device instance.
151 *
152 * The template is used when creating new completion tasks.
153 *
154 * @returns VBox status code.
155 * @param pVM Pointer to the shared VM structure.
156 * @param pUsbIns The USB device instance.
157 * @param ppTemplate Where to store the template pointer on success.
158 * @param pfnCompleted The completion callback routine.
159 * @param pszDesc Description.
160 */
161PDMR3DECL(int) PDMR3AsyncCompletionTemplateCreateUsb(PVM pVM, PPDMUSBINS pUsbIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEUSB pfnCompleted, const char *pszDesc);
162
163/**
164 * Creates a async completion template for internally by the VMM.
165 *
166 * The template is used when creating new completion tasks.
167 *
168 * @returns VBox status code.
169 * @param pVM Pointer to the shared VM structure.
170 * @param ppTemplate Where to store the template pointer on success.
171 * @param pfnCompleted The completion callback routine.
172 * @param pvUser2 The 2nd user argument for the callback.
173 * @param pszDesc Description.
174 */
175PDMR3DECL(int) PDMR3AsyncCompletionTemplateCreateInternal(PVM pVM, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEINT pfnCompleted, void *pvUser2, const char *pszDesc);
176
177/**
178 * Destroys the specified async completion template.
179 *
180 * @returns VBox status codes:
181 * @retval VINF_SUCCESS on success.
182 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if the template is still in use.
183 *
184 * @param pTemplate The template in question.
185 */
186PDMR3DECL(int) PDMR3AsyncCompletionTemplateDestroy(PPDMASYNCCOMPLETIONTEMPLATE pTemplate);
187
188/**
189 * Destroys all the specified async completion templates for the given device instance.
190 *
191 * @returns VBox status codes:
192 * @retval VINF_SUCCESS on success.
193 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use.
194 *
195 * @param pVM Pointer to the shared VM structure.
196 * @param pDevIns The device instance.
197 */
198PDMR3DECL(int) PDMR3AsyncCompletionTemplateDestroyDevice(PVM pVM, PPDMDEVINS pDevIns);
199
200/**
201 * Destroys all the specified async completion templates for the given driver instance.
202 *
203 * @returns VBox status codes:
204 * @retval VINF_SUCCESS on success.
205 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use.
206 *
207 * @param pVM Pointer to the shared VM structure.
208 * @param pDrvIns The driver instance.
209 */
210PDMR3DECL(int) PDMR3AsyncCompletionTemplateDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns);
211
212/**
213 * Destroys all the specified async completion templates for the given USB device instance.
214 *
215 * @returns VBox status codes:
216 * @retval VINF_SUCCESS on success.
217 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use.
218 *
219 * @param pVM Pointer to the shared VM structure.
220 * @param pUsbIns The USB device instance.
221 */
222PDMR3DECL(int) PDMR3AsyncCompletionTemplateDestroyUsb(PVM pVM, PPDMUSBINS pUsbIns);
223
224/**
225 * Async completion task type
226 */
227typedef enum PDMASYNCCOMPLETIONTASKTYPE
228{
229 /** Socket. */
230 PDMASYNCCOMPLETIONTASKTYPE_SOCKET = 0,
231 /** Host OS specific. */
232 PDMASYNCCOMPLETIONTASKTYPE_HOST,
233 /** Number of supported backends. This has to be last entry! */
234 PDMASYNCCOMPLETIONTASKTYPE_SUPPORTED
235} PDMASYNCCOMPLETIONTASKTYPE;
236
237/**
238 * Get the backend name of a task type.
239 *
240 * @returns Name of the backend.
241 * @param enmTaskType The task type to get the backend name from.
242 */
243PDMR3DECL(const char *) PDMR3AsyncCompletionGetBackendName(PDMASYNCCOMPLETIONTASKTYPE enmTaskType);
244
245/**
246 * Creates a completion task.
247 *
248 * @returns VBox status code.
249 * @param ppTask Where to store the task handle on success.
250 * @param pTemplate The async completion template.
251 * @param enmType The type of the task.
252 * @param pvCtx The task specific context.
253 * @param pvUser The user argument for the callback.
254 */
255PDMR3DECL(int) PDMR3AsyncCompletionTaskCreate(PPPDMASYNCCOMPLETIONTASK ppTask, PPDMASYNCCOMPLETIONTEMPLATE pTemplate, PDMASYNCCOMPLETIONTASKTYPE enmType, void *pvCtx, void *pvUser);
256
257/**
258 * Associate a task with a type specific context.
259 *
260 * @returns VBox status code.
261 * @param pTask The task to associate the context with.
262 * @param pvCtx Pointer to the context.
263 */
264PDMR3DECL(int) PDMR3AsyncCompletionTaskAssociate(PPDMASYNCCOMPLETIONTASK pTask, void *pvCtx);
265
266/**
267 * Submit an array of tasks for processing
268 * The tasks must have a type specific context.
269 *
270 * @returns VBox status code.
271 * @param apTasks Array of tasks which should be processed.
272 * @param cTasks Number of tasks in the array which should be processed.
273 */
274PDMR3DECL(int) PDMR3AsyncCompletionTaskSubmit(PPDMASYNCCOMPLETIONTASK apTasks[], unsigned cTasks);
275
276/**
277 * Sets the user argument of a completion task.
278 *
279 * @returns VBox status code.
280 * @param pTask The async completion task.
281 * @param pvUser The user argument for the callback.
282 */
283PDMR3DECL(int) PDMR3AsyncCompletionTaskSetUserArg(PPDMASYNCCOMPLETIONTASK pTask, void *pvUser);
284
285/**
286 * Suspends a async completion task.
287 *
288 * @returns VBox status codes:
289 * @retval VINF_SUCCESS on success.
290 * @retval VERR_PDM_ASYNC_COMPLETION_ALREADY_SUSPENDED if already suspended.
291 * @retval VERR_INVALID_HANDLE if pTask is invalid (asserts).
292 * @param pTask The async completion task.
293 */
294PDMR3DECL(int) PDMR3AsyncCompletionTaskSuspend(PPDMASYNCCOMPLETIONTASK pTask);
295
296/**
297 * Suspends a async completion task.
298 *
299 * @returns VBox status codes:
300 * @retval VINF_SUCCESS on success.
301 * @retval VERR_PDM_ASYNC_COMPLETION_NOT_SUSPENDED if not suspended.
302 * @retval VERR_INVALID_HANDLE if pTask is invalid (asserts).
303 * @param pTask The async completion task.
304 */
305PDMR3DECL(int) PDMR3AsyncCompletionTaskResume(PPDMASYNCCOMPLETIONTASK pTask);
306
307/**
308 * Cancels a async completion task.
309 * The task doesn't have to be suspended.
310 *
311 * @returns VBox status code
312 * @param pTask The Task to cancel.
313 */
314PDMR3DECL(int) PDMR3AsyncCompletionTaskCancel(PPDMASYNCCOMPLETIONTASK pTask);
315
316/**
317 * Destroys a async completion task.
318 *
319 * The task doesn't have to be suspended or anything.
320 *
321 * @returns VBox status codes:
322 * @retval VINF_SUCCESS on success.
323 * @retval VERR_INVALID_HANDLE if pTask is invalid but not NIL (asserts).
324 * @param pTask The async completion task.
325 */
326PDMR3DECL(int) PDMR3AsyncCompletionTaskDestroy(PPDMASYNCCOMPLETIONTASK pTask);
327
328/*
329 * Host specific wrapper functions for the above API
330 */
331#if defined(RT_OS_LINUX)
332
333struct iocb;
334
335/**
336 * Creates a completion task for an IO operation on Linux.
337 *
338 * The pvCtx callback argument will be pIoCB.
339 *
340 * @returns VBox status code.
341 * @param ppTask Where to store the task handle on success.
342 * @param pTemplate The async completion template.
343 * @param pIoCB The asynchronous I/O control block to wait for.
344 * @param pvUser The user argument for the callback.
345 */
346DECLINLINE(int) PDMR3AsyncCompletionCreateLnxIO(PPPDMASYNCCOMPLETIONTASK ppTask, PPDMASYNCCOMPLETIONTEMPLATE pTemplate, const struct iocb *pIoCB, void *pvUser)
347{
348 int rc = VINF_SUCCESS;
349 PPDMASYNCCOMPLETIONTASK pTask;
350
351 rc = PDMR3AsyncCompletionTaskCreate(&pTask, pTemplate, PDMASYNCCOMPLETIONTASKTYPE_HOST, (void *)pIoCB, pvUser);
352 if (VBOX_FAILURE(rc))
353 {
354 AssertMsgFailed(("Creating Linux task failed\n"));
355 return rc;
356 }
357
358 *ppTask = pTask;
359
360 return rc;
361}
362#endif /* RT_OS_LINUX */
363
364#if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX)
365
366struct aiocb;
367
368/**
369 * Creates a completion task for an AIO operation on Unix like systems like Solaris, Darwin or FreeBSD.
370 * This method must be used too on Linux if the real asynchronous solution is not available.
371 *
372 * The pvCtx callback argument will be pAioCB.
373 *
374 * @returns VBox status code.
375 * @param ppTask Where to store the task handle on success.
376 * @param pTemplate The async completion template.
377 * @param pIoCB The asynchronous I/O control block to wait for.
378 * @param pvUser The user argument for the callback.
379 */
380DECLINLINE(int) PDMR3AsyncCompletionCreateUnxAIO(PPPDMASYNCCOMPLETIONTASK ppTask, PPDMASYNCCOMPLETIONTEMPLATE pTemplate, const struct aiocb *pAioCB, void *pvUser)
381{
382 int rc = VINF_SUCCESS;
383 PPDMASYNCCOMPLETIONTASK pTask;
384
385 rc = PDMR3AsyncCompletionTaskCreate(&pTask, pTemplate, PDMASYNCCOMPLETIONTASKTYPE_HOST, (void *)pAioCB, pvUser);
386 if (VBOX_FAILURE(rc))
387 {
388 AssertMsgFailed(("Creating AIO task failed\n"));
389 return rc;
390 }
391
392 *ppTask = pTask;
393
394 return rc;
395}
396#endif /* RT_OS_SOLARIS || RT_OS_DARWIN || RT_OS_FREEBSD || RT_OS_LINUX */
397
398#ifdef RT_OS_OS2
399/**
400 * Creates a completion task for an event semaphore on OS/2.
401 *
402 * The pvCtx callback argument will be hev.
403 *
404 * @returns VBox status code.
405 * @param ppTask Where to store the task handle on success.
406 * @param pTemplate The async completion template.
407 * @param hev The handle of the event semaphore to wait on.
408 * @param pvUser The user argument for the callback.
409 */
410DECLINLINE(int) PDMR3AsyncCompletionCreateOs2Event(PPPDMASYNCCOMPLETIONTASK ppTask, PPDMASYNCCOMPLETIONTEMPLATE pTemplate, unsigned long hev, void *pvUser)
411{
412 int rc = VINF_SUCCESS;
413 PPDMASYNCCOMPLETIONTASK pTask;
414
415 rc = PDMR3AsyncCompletionTaskCreate(&pTask, pTemplate, PDMASYNCCOMPLETIONTASKTYPE_HOST, (void *)&hev, pvUser);
416 if (VBOX_FAILURE(rc))
417 {
418 AssertMsgFailed(("Creating OS/2 task failed\n"));
419 return rc;
420 }
421
422 *ppTask = pTask;
423
424 return rc;
425}
426#endif /* RT_OS_OS2 */
427
428#ifdef RT_OS_WINDOWS
429/**
430 * Creates a completion task for an object on Windows.
431 *
432 * The pvCtx callback argument will be hObject.
433 *
434 * @returns VBox status code.
435 * @param ppTask Where to store the task handle on success.
436 * @param pTemplate The async completion template.
437 * @param hObject The object to wait for.
438 * @param pvUser The user argument for the callback.
439 */
440DECLINLINE(int) PDMR3AsyncCompletionCreateWinObject(PPPDMASYNCCOMPLETIONTASK ppTask, PPDMASYNCCOMPLETIONTEMPLATE pTemplate, void *hObject, void *pvUser)
441{
442 int rc = VINF_SUCCESS;
443 PPDMASYNCCOMPLETIONTASK pTask;
444
445 rc = PDMR3AsyncCompletionTaskCreate(&pTask, pTemplate, PDMASYNCCOMPLETIONTASKTYPE_HOST, (void *)hObject, pvUser);
446 if (VBOX_FAILURE(rc))
447 {
448 AssertMsgFailed(("Creating Windows task failed\n"));
449 return rc;
450 }
451
452 *ppTask = pTask;
453
454 return rc;
455}
456#endif /* RT_OS_WINDOWS */
457
458/**
459 * Socket completion context (pvCtx).
460 */
461typedef struct PDMASYNCCOMPLETIONSOCKET
462{
463 /** The socket. */
464 RTSOCKET Socket;
465 /** Readable. */
466 bool fReadable;
467 /** Writable. */
468 bool fWriteable;
469 /** Exceptions. */
470 bool fXcpt;
471} PDMASYNCCOMPLETIONSOCKET;
472/** Pointer to a socket completion context. */
473typedef PDMASYNCCOMPLETIONSOCKET *PPDMASYNCCOMPLETIONSOCKET;
474
475/**
476 * Creates a completion task for a socket.
477 *
478 * The pvCtx callback argument will be pointing to a PDMASYNCCOMPLETIONSOCKET structure.
479 *
480 * @returns VBox status code.
481 * @param ppTask Where to store the task handle on success.
482 * @param pTemplate The async completion template.
483 * @param Socket The socket.
484 * @param fReadable Whether to callback when the socket becomes readable.
485 * @param fWriteable Whether to callback when the socket becomes writable.
486 * @param fXcpt Whether to callback on exception.
487 * @param pvUser The user argument for the callback.
488 */
489DECLINLINE(int) PDMR3AsyncCompletionCreateSocket(PPPDMASYNCCOMPLETIONTASK ppTask, PPDMASYNCCOMPLETIONTEMPLATE pTemplate, RTSOCKET Socket, bool fReadable, bool fWriteable, bool fXcpt, void *pvUser)
490{
491 int rc = VINF_SUCCESS;
492 PPDMASYNCCOMPLETIONTASK pTask;
493 PDMASYNCCOMPLETIONSOCKET SocketContext;
494
495 SocketContext.Socket = Socket;
496 SocketContext.fReadable = fReadable;
497 SocketContext.fWriteable = fWriteable;
498 SocketContext.fXcpt = fXcpt;
499
500 rc = PDMR3AsyncCompletionTaskCreate(&pTask, pTemplate, PDMASYNCCOMPLETIONTASKTYPE_SOCKET, (void *)&SocketContext, pvUser);
501 if (VBOX_FAILURE(rc))
502 {
503 AssertMsgFailed(("Creating Socket task failed\n"));
504 return rc;
505 }
506
507 *ppTask = pTask;
508
509 return rc;
510}
511
512/**
513 * Modifies a socket completion task.
514 *
515 * @returns VBox status code.
516 * @retval VINF_SUCCESS on success.
517 * @retval VERR_NOT_SUPPORTED if the task isn't a socket task.
518 * @param pTemplate The async completion template.
519 * @param Socket The socket
520 * @param fReadable Whether to callback when the socket becomes readable.
521 * @param fWriteable Whether to callback when the socket becomes writable.
522 * @param fXcpt Whether to callback on exception.
523 */
524DECLINLINE(int) PDMR3AsyncCompletionModifySocket(PPDMASYNCCOMPLETIONTASK pTask, RTSOCKET Socket, bool fReadable, bool fWriteable, bool fXcpt)
525{
526 int rc = VINF_SUCCESS;
527 PDMASYNCCOMPLETIONSOCKET SocketContext;
528
529 SocketContext.Socket = Socket;
530 SocketContext.fReadable = fReadable;
531 SocketContext.fWriteable = fWriteable;
532 SocketContext.fXcpt = fXcpt;
533
534 rc = PDMR3AsyncCompletionTaskAssociate(pTask, &SocketContext);
535 if (VBOX_FAILURE(rc))
536 {
537 AssertMsgFailed(("Modifying Socket task failed\n"));
538 return rc;
539 }
540
541 return rc;
542}
543
544/** @} */
545
546__END_DECLS
547
548#endif
549
550
551
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use