VirtualBox

source: vbox/trunk/include/iprt/semaphore.h@ 23702

Last change on this file since 23702 was 21513, checked in by vboxsync, 15 years ago

iprt/semaphore.h,types.h: Defined a new semaphore type for use with the logger.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.5 KB
Line 
1/** @file
2 * IPRT - Semaphore.
3 */
4
5/*
6 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * The contents of this file may alternatively be used under the terms
17 * of the Common Development and Distribution License Version 1.0
18 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
19 * VirtualBox OSE distribution, in which case the provisions of the
20 * CDDL are applicable instead of those of the GPL.
21 *
22 * You may elect to license modified versions of this file under the
23 * terms and conditions of either the GPL or the CDDL or both.
24 *
25 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
26 * Clara, CA 95054 USA or visit http://www.sun.com if you need
27 * additional information or have any questions.
28 */
29
30#ifndef ___iprt_semaphore_h
31#define ___iprt_semaphore_h
32
33#include <iprt/cdefs.h>
34#include <iprt/types.h>
35
36
37RT_C_DECLS_BEGIN
38
39/** @defgroup grp_rt_sems RTSem - Semaphores
40 * @ingroup grp_rt
41 * @{
42 */
43
44
45/** @defgroup grp_rt_sems_event RTSemEvent - Single Release Event Semaphores
46 * @{ */
47
48/**
49 * Create a event semaphore.
50 *
51 * @returns iprt status code.
52 * @param pEventSem Where to store the event semaphore handle.
53 */
54RTDECL(int) RTSemEventCreate(PRTSEMEVENT pEventSem);
55
56/**
57 * Destroy an event semaphore.
58 *
59 * @returns iprt status code.
60 * @param EventSem Handle of the
61 */
62RTDECL(int) RTSemEventDestroy(RTSEMEVENT EventSem);
63
64/**
65 * Signal an event semaphore.
66 *
67 * The event semaphore will be signaled and automatically reset
68 * after exactly one thread have successfully returned from
69 * RTSemEventWait() after waiting/polling on that semaphore.
70 *
71 * @returns iprt status code.
72 * @param EventSem The event semaphore to signal.
73 */
74RTDECL(int) RTSemEventSignal(RTSEMEVENT EventSem);
75
76/**
77 * Wait for the event semaphore to be signaled, resume on interruption.
78 *
79 * This function will resume if the wait is interrupted by an async
80 * system event (like a unix signal) or similar.
81 *
82 * @returns iprt status code.
83 * Will not return VERR_INTERRUPTED.
84 * @param EventSem The event semaphore to wait on.
85 * @param cMillies Number of milliseconds to wait.
86 */
87RTDECL(int) RTSemEventWait(RTSEMEVENT EventSem, unsigned cMillies);
88
89/**
90 * Wait for the event semaphore to be signaled, return on interruption.
91 *
92 * This function will not resume the wait if interrupted.
93 *
94 * @returns iprt status code.
95 * @param EventSem The event semaphore to wait on.
96 * @param cMillies Number of milliseconds to wait.
97 */
98RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT EventSem, unsigned cMillies);
99
100/** @} */
101
102
103/** @defgroup grp_rt_sems_event_multi RTSemEventMulti - Multiple Release Event Semaphores
104 * @{ */
105
106/**
107 * Create a event multi semaphore.
108 *
109 * @returns iprt status code.
110 * @param pEventMultiSem Where to store the event multi semaphore handle.
111 */
112RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI pEventMultiSem);
113
114/**
115 * Destroy an event multi semaphore.
116 *
117 * @returns iprt status code.
118 * @param EventMultiSem The event multi sempahore to destroy.
119 */
120RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI EventMultiSem);
121
122/**
123 * Signal an event multi semaphore.
124 *
125 * @returns iprt status code.
126 * @param EventMultiSem The event multi semaphore to signal.
127 */
128RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI EventMultiSem);
129
130/**
131 * Resets an event multi semaphore to non-signaled state.
132 *
133 * @returns iprt status code.
134 * @param EventMultiSem The event multi semaphore to reset.
135 */
136RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI EventMultiSem);
137
138/**
139 * Wait for the event multi semaphore to be signaled, resume on interruption.
140 *
141 * This function will resume if the wait is interrupted by an async
142 * system event (like a unix signal) or similar.
143 *
144 * @returns iprt status code.
145 * Will not return VERR_INTERRUPTED.
146 * @param EventMultiSem The event multi semaphore to wait on.
147 * @param cMillies Number of milliseconds to wait.
148 */
149RTDECL(int) RTSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies);
150
151
152/**
153 * Wait for the event multi semaphore to be signaled, return on interruption.
154 *
155 * This function will not resume the wait if interrupted.
156 *
157 * @returns iprt status code.
158 * @param EventMultiSem The event multi semaphore to wait on.
159 * @param cMillies Number of milliseconds to wait.
160 */
161RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies);
162
163/** @} */
164
165
166/** @defgroup grp_rt_sems_mutex RTMutex - Mutex semaphores.
167 *
168 * @remarks These can be pretty heavy handed. Fast mutexes or critical sections
169 * is usually what you need.
170 *
171 * @{ */
172
173/**
174 * Create a mutex semaphore.
175 *
176 * @returns iprt status code.
177 * @param pMutexSem Where to store the mutex semaphore handle.
178 */
179RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX pMutexSem);
180
181/**
182 * Destroy a mutex semaphore.
183 *
184 * @returns iprt status code.
185 * @param MutexSem The mutex semaphore to destroy.
186 */
187RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX MutexSem);
188
189/**
190 * Request ownership of a mutex semaphore, resume on interruption.
191 *
192 * This function will resume if the wait is interrupted by an async
193 * system event (like a unix signal) or similar.
194 *
195 * The same thread may request a mutex semaphore multiple times,
196 * a nested counter is kept to make sure it's released on the right
197 * RTSemMutexRelease() call.
198 *
199 * @returns iprt status code.
200 * Will not return VERR_INTERRUPTED.
201 * @param MutexSem The mutex semaphore to request ownership over.
202 * @param cMillies The number of milliseconds to wait.
203 */
204RTDECL(int) RTSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies);
205
206/**
207 * Request ownership of a mutex semaphore, return on interruption.
208 *
209 * This function will not resume the wait if interrupted.
210 *
211 * The same thread may request a mutex semaphore multiple times,
212 * a nested counter is kept to make sure it's released on the right
213 * RTSemMutexRelease() call.
214 *
215 * @returns iprt status code.
216 * @param MutexSem The mutex semaphore to request ownership over.
217 * @param cMillies The number of milliseconds to wait.
218 */
219RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX MutexSem, unsigned cMillies);
220
221/**
222 * Release the ownership of a mutex semaphore.
223 *
224 * @returns iprt status code.
225 * @param MutexSem The mutex to release the ownership of.
226 * It goes without saying the the calling thread must own it.
227 */
228RTDECL(int) RTSemMutexRelease(RTSEMMUTEX MutexSem);
229
230/** @} */
231
232
233/** @defgroup grp_rt_sems_fast_mutex RTSemFastMutex - Fast Mutex Semaphores
234 * @{ */
235
236/**
237 * Create a fast mutex semaphore.
238 *
239 * @returns iprt status code.
240 * @param pMutexSem Where to store the mutex semaphore handle.
241 *
242 * @remarks Fast mutex semaphores are not recursive.
243 */
244RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX pMutexSem);
245
246/**
247 * Destroy a fast mutex semaphore.
248 *
249 * @returns iprt status code.
250 * @param MutexSem The mutex semaphore to destroy.
251 */
252RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX MutexSem);
253
254/**
255 * Request ownership of a fast mutex semaphore.
256 *
257 * The same thread may request a mutex semaphore multiple times,
258 * a nested counter is kept to make sure it's released on the right
259 * RTSemMutexRelease() call.
260 *
261 * @returns iprt status code.
262 * @param MutexSem The mutex semaphore to request ownership over.
263 */
264RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX MutexSem);
265
266/**
267 * Release the ownership of a fast mutex semaphore.
268 *
269 * @returns iprt status code.
270 * @param MutexSem The mutex to release the ownership of.
271 * It goes without saying the the calling thread must own it.
272 */
273RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX MutexSem);
274
275/** @} */
276
277
278/** @defgroup grp_rt_sems_spin_mutex RTSemSpinMutex - Spinning Mutex Semaphores
279 *
280 * Very adaptive kind of mutex semaphore tailored for the ring-0 logger.
281 *
282 * @{ */
283
284/**
285 * Creates a spinning mutex semaphore.
286 *
287 * @returns iprt status code.
288 * @retval VERR_INVALID_PARAMETER on invalid flags.
289 * @retval VERR_NO_MEMORY if out of memory for the semaphore structure and
290 * handle.
291 *
292 * @param phSpinMtx Where to return the handle to the create semaphore.
293 * @param fFlags Flags, see RTSEMSPINMUTEX_FLAGS_XXX.
294 */
295RTDECL(int) RTSemSpinMutexCreate(PRTSEMSPINMUTEX phSpinMtx, uint32_t fFlags);
296
297/** @name RTSemSpinMutexCreate flags.
298 * @{ */
299/** Always take the semaphore in a IRQ safe way.
300 * (In plain words: always disable interrupts.) */
301#define RTSEMSPINMUTEX_FLAGS_IRQ_SAFE RT_BIT_32(0)
302/** Mask of valid flags. */
303#define RTSEMSPINMUTEX_FLAGS_VALID_MASK UINT32_C(0x00000001)
304/** @} */
305
306/**
307 * Destroys a spinning mutex semaphore.
308 *
309 * @returns iprt status code.
310 * @retval VERR_INVALID_HANDLE (or crash) if the handle is invalid. (NIL will
311 * not cause this status.)
312 *
313 * @param hSpinMtx The semaphore handle. NIL_RTSEMSPINMUTEX is ignored
314 * quietly (VINF_SUCCESS).
315 */
316RTDECL(int) RTSemSpinMutexDestroy(RTSEMSPINMUTEX hSpinMtx);
317
318/**
319 * Request the spinning mutex semaphore.
320 *
321 * This may block if the context we're called in allows this. If not it will
322 * spin. If called in an interrupt context, we will only spin if the current
323 * owner isn't interrupted. Also, on some systems it is not always possible to
324 * wake up blocking threads in all contexts, so, which will either be indicated
325 * by returning VERR_SEM_BAD_CONTEXT or by temporarily switching the semaphore
326 * into pure spinlock state.
327 *
328 * Preemption will be disabled upon return. IRQs may also be disabled.
329 *
330 * @returns iprt status code.
331 * @retval VERR_SEM_BAD_CONTEXT if the context it's called in isn't suitable
332 * for releasing it if someone is sleeping on it.
333 * @retval VERR_SEM_DESTROYED if destroyed.
334 * @retval VERR_SEM_NESTED if held by the caller. Asserted.
335 * @retval VERR_INVALID_HANDLE if the handle is invalid. Asserted
336 *
337 * @param hSpinMtx The semaphore handle.
338 */
339RTDECL(int) RTSemSpinMutexRequest(RTSEMSPINMUTEX hSpinMtx);
340
341/**
342 * Like RTSemSpinMutexRequest but it won't block or spin if the semaphore is
343 * held by someone else.
344 *
345 * @returns iprt status code.
346 * @retval VERR_SEM_BUSY if held by someone else.
347 * @retval VERR_SEM_DESTROYED if destroyed.
348 * @retval VERR_SEM_NESTED if held by the caller. Asserted.
349 * @retval VERR_INVALID_HANDLE if the handle is invalid. Asserted
350 *
351 * @param hSpinMtx The semaphore handle.
352 */
353RTDECL(int) RTSemSpinMutexTryRequest(RTSEMSPINMUTEX hSpinMtx);
354
355/**
356 * Releases the semaphore previously acquired by RTSemSpinMutexRequest or
357 * RTSemSpinMutexTryRequest.
358 *
359 * @returns iprt status code.
360 * @retval VERR_SEM_DESTROYED if destroyed.
361 * @retval VERR_NOT_OWNER if not owner. Asserted.
362 * @retval VERR_INVALID_HANDLE if the handle is invalid. Asserted.
363 *
364 * @param hSpinMtx The semaphore handle.
365 */
366RTDECL(int) RTSemSpinMutexRelease(RTSEMSPINMUTEX hSpinMtx);
367
368/** @} */
369
370
371/** @defgroup grp_rt_sem_rw RTSemRW - Read / Write Semaphores
372 * @{ */
373
374/**
375 * Creates a read/write semaphore.
376 *
377 * @returns iprt status code.
378 * @param pRWSem Where to store the handle to the created RW semaphore.
379 */
380RTDECL(int) RTSemRWCreate(PRTSEMRW pRWSem);
381
382/**
383 * Destroys a read/write semaphore.
384 *
385 * @returns iprt status code.
386 * @param RWSem The Read/Write semaphore to destroy.
387 */
388RTDECL(int) RTSemRWDestroy(RTSEMRW RWSem);
389
390/**
391 * Request read access to a read/write semaphore, resume on interruption
392 *
393 * @returns iprt status code.
394 * @retval VINF_SUCCESS on success.
395 * @retval VERR_INTERRUPT if the wait was interrupted.
396 * @retval VERR_INVALID_HANDLE if RWSem is invalid.
397 *
398 * @param RWSem The Read/Write semaphore to request read access to.
399 * @param cMillies The number of milliseconds to wait.
400 */
401RTDECL(int) RTSemRWRequestRead(RTSEMRW RWSem, unsigned cMillies);
402
403/**
404 * Request read access to a read/write semaphore, return on interruption
405 *
406 * @returns iprt status code.
407 * @retval VINF_SUCCESS on success.
408 * @retval VERR_INTERRUPT if the wait was interrupted.
409 * @retval VERR_INVALID_HANDLE if RWSem is invalid.
410 *
411 * @param RWSem The Read/Write semaphore to request read access to.
412 * @param cMillies The number of milliseconds to wait.
413 */
414RTDECL(int) RTSemRWRequestReadNoResume(RTSEMRW RWSem, unsigned cMillies);
415
416/**
417 * Release read access to a read/write semaphore.
418 *
419 * @returns iprt status code.
420 * @param RWSem The Read/Write sempahore to release read access to.
421 * Goes without saying that caller must have read access to the sem.
422 */
423RTDECL(int) RTSemRWReleaseRead(RTSEMRW RWSem);
424
425/**
426 * Request write access to a read/write semaphore, resume on interruption.
427 *
428 * @returns iprt status code.
429 * @retval VINF_SUCCESS on success.
430 * @retval VERR_DEADLOCK if the caller owned the read lock.
431 * @retval VERR_INVALID_HANDLE if RWSem is invalid.
432 *
433 * @param RWSem The Read/Write semaphore to request write access to.
434 * @param cMillies The number of milliseconds to wait.
435 */
436RTDECL(int) RTSemRWRequestWrite(RTSEMRW RWSem, unsigned cMillies);
437
438/**
439 * Request write access to a read/write semaphore, return on interruption.
440 *
441 * @returns iprt status code.
442 * @retval VINF_SUCCESS on success.
443 * @retval VERR_INTERRUPT if the wait was interrupted.
444 * @retval VERR_DEADLOCK if the caller owned the read lock.
445 * @retval VERR_INVALID_HANDLE if RWSem is invalid.
446 *
447 * @param RWSem The Read/Write semaphore to request write access to.
448 * @param cMillies The number of milliseconds to wait.
449 */
450RTDECL(int) RTSemRWRequestWriteNoResume(RTSEMRW RWSem, unsigned cMillies);
451
452/**
453 * Release write access to a read/write semaphore.
454 *
455 * @returns iprt status code.
456 * @param RWSem The Read/Write sempahore to release read access to.
457 * Goes without saying that caller must have write access to the sem.
458 */
459RTDECL(int) RTSemRWReleaseWrite(RTSEMRW RWSem);
460
461/**
462 * Checks if the caller is the exclusive semaphore owner.
463 *
464 * @returns true / false accoringly.
465 * @param RWSem The Read/Write semaphore in question.
466 */
467RTDECL(bool) RTSemRWIsWriteOwner(RTSEMRW RWSem);
468
469/**
470 * Gets the write recursion count.
471 *
472 * @returns The write recursion count (0 if bad semaphore handle).
473 * @param RWSem The Read/Write semaphore in question.
474 */
475RTDECL(uint32_t) RTSemRWGetWriteRecursion(RTSEMRW RWSem);
476
477/**
478 * Gets the read recursion count of the current writer.
479 *
480 * @returns The read recursion count (0 if bad semaphore handle).
481 * @param RWSem The Read/Write semaphore in question.
482 */
483RTDECL(uint32_t) RTSemRWGetWriterReadRecursion(RTSEMRW RWSem);
484
485/** @} */
486
487
488/** @defgroup grp_rt_sems_pingpong RTSemPingPong - Ping-Pong Construct
489 * @{ */
490
491/**
492 * Ping-pong speaker
493 */
494typedef enum RTPINGPONGSPEAKER
495{
496 /** Not initialized. */
497 RTPINGPONGSPEAKER_UNINITIALIZE = 0,
498 /** Ping is speaking, Pong is waiting. */
499 RTPINGPONGSPEAKER_PING,
500 /** Pong is signaled, Ping is waiting. */
501 RTPINGPONGSPEAKER_PONG_SIGNALED,
502 /** Pong is speaking, Ping is waiting. */
503 RTPINGPONGSPEAKER_PONG,
504 /** Ping is signaled, Pong is waiting. */
505 RTPINGPONGSPEAKER_PING_SIGNALED,
506 /** Hack to ensure that it's at least 32-bits wide. */
507 RTPINGPONGSPEAKER_HACK = 0x7fffffff
508} RTPINGPONGSPEAKER;
509
510/**
511 * Ping-Pong construct.
512 *
513 * Two threads, one saying Ping and the other saying Pong. The construct
514 * makes sure they don't speak out of turn and that they can wait and poll
515 * on the conversation.
516 */
517typedef struct RTPINGPONG
518{
519 /** The semaphore the Ping thread waits on. */
520 RTSEMEVENT Ping;
521 /** The semaphore the Pong thread waits on. */
522 RTSEMEVENT Pong;
523 /** The current speaker. */
524 volatile RTPINGPONGSPEAKER enmSpeaker;
525#if HC_ARCH_BITS == 64
526 /** Padding the structure to become a multiple of sizeof(RTHCPTR). */
527 uint32_t u32Padding;
528#endif
529} RTPINGPONG;
530/** Pointer to Ping-Pong construct. */
531typedef RTPINGPONG *PRTPINGPONG;
532
533/**
534 * Init a Ping-Pong construct.
535 *
536 * @returns iprt status code.
537 * @param pPP Pointer to the ping-pong structure which needs initialization.
538 */
539RTDECL(int) RTSemPingPongInit(PRTPINGPONG pPP);
540
541/**
542 * Deletes a Ping-Pong construct.
543 *
544 * @returns iprt status code.
545 * @param pPP Pointer to the ping-pong structure which is to be destroyed.
546 * (I.e. put into uninitialized state.)
547 */
548RTDECL(int) RTSemPingPongDelete(PRTPINGPONG pPP);
549
550/**
551 * Signals the pong thread in a ping-pong construct. (I.e. sends ping.)
552 * This is called by the ping thread.
553 *
554 * @returns iprt status code.
555 * @param pPP Pointer to the ping-pong structure to ping.
556 */
557RTDECL(int) RTSemPing(PRTPINGPONG pPP);
558
559/**
560 * Signals the ping thread in a ping-pong construct. (I.e. sends pong.)
561 * This is called by the pong thread.
562 *
563 * @returns iprt status code.
564 * @param pPP Pointer to the ping-pong structure to pong.
565 */
566RTDECL(int) RTSemPong(PRTPINGPONG pPP);
567
568/**
569 * Wait function for the ping thread.
570 *
571 * @returns iprt status code.
572 * Will not return VERR_INTERRUPTED.
573 * @param pPP Pointer to the ping-pong structure to wait on.
574 * @param cMillies Number of milliseconds to wait.
575 */
576RTDECL(int) RTSemPingWait(PRTPINGPONG pPP, unsigned cMillies);
577
578/**
579 * Wait function for the pong thread.
580 *
581 * @returns iprt status code.
582 * Will not return VERR_INTERRUPTED.
583 * @param pPP Pointer to the ping-pong structure to wait on.
584 * @param cMillies Number of milliseconds to wait.
585 */
586RTDECL(int) RTSemPongWait(PRTPINGPONG pPP, unsigned cMillies);
587
588
589/**
590 * Checks if the pong thread is speaking.
591 *
592 * @returns true / false.
593 * @param pPP Pointer to the ping-pong structure.
594 * @remark This is NOT the same as !RTSemPongIsSpeaker().
595 */
596DECLINLINE(bool) RTSemPingIsSpeaker(PRTPINGPONG pPP)
597{
598 RTPINGPONGSPEAKER enmSpeaker = pPP->enmSpeaker;
599 return enmSpeaker == RTPINGPONGSPEAKER_PING;
600}
601
602
603/**
604 * Checks if the pong thread is speaking.
605 *
606 * @returns true / false.
607 * @param pPP Pointer to the ping-pong structure.
608 * @remark This is NOT the same as !RTSemPingIsSpeaker().
609 */
610DECLINLINE(bool) RTSemPongIsSpeaker(PRTPINGPONG pPP)
611{
612 RTPINGPONGSPEAKER enmSpeaker = pPP->enmSpeaker;
613 return enmSpeaker == RTPINGPONGSPEAKER_PONG;
614}
615
616
617/**
618 * Checks whether the ping thread should wait.
619 *
620 * @returns true / false.
621 * @param pPP Pointer to the ping-pong structure.
622 * @remark This is NOT the same as !RTSemPongShouldWait().
623 */
624DECLINLINE(bool) RTSemPingShouldWait(PRTPINGPONG pPP)
625{
626 RTPINGPONGSPEAKER enmSpeaker = pPP->enmSpeaker;
627 return enmSpeaker == RTPINGPONGSPEAKER_PONG
628 || enmSpeaker == RTPINGPONGSPEAKER_PONG_SIGNALED
629 || enmSpeaker == RTPINGPONGSPEAKER_PING_SIGNALED;
630}
631
632
633/**
634 * Checks whether the pong thread should wait.
635 *
636 * @returns true / false.
637 * @param pPP Pointer to the ping-pong structure.
638 * @remark This is NOT the same as !RTSemPingShouldWait().
639 */
640DECLINLINE(bool) RTSemPongShouldWait(PRTPINGPONG pPP)
641{
642 RTPINGPONGSPEAKER enmSpeaker = pPP->enmSpeaker;
643 return enmSpeaker == RTPINGPONGSPEAKER_PING
644 || enmSpeaker == RTPINGPONGSPEAKER_PING_SIGNALED
645 || enmSpeaker == RTPINGPONGSPEAKER_PONG_SIGNALED;
646}
647
648/** @} */
649
650/** @} */
651
652RT_C_DECLS_END
653
654#endif
655
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