VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/semsrw-generic.cpp@ 5999

Last change on this file since 5999 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 19.4 KB
Line 
1/* $Id: semsrw-generic.cpp 5999 2007-12-07 15:05:06Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Read-Write Semaphore, Generic.
4 *
5 * This is a generic implementation for OSes which don't have
6 * native RW semaphores.
7 */
8
9/*
10 * Copyright (C) 2006-2007 innotek GmbH
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 *
20 * The contents of this file may alternatively be used under the terms
21 * of the Common Development and Distribution License Version 1.0
22 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
23 * VirtualBox OSE distribution, in which case the provisions of the
24 * CDDL are applicable instead of those of the GPL.
25 *
26 * You may elect to license modified versions of this file under the
27 * terms and conditions of either the GPL or the CDDL or both.
28 */
29
30/** @todo fix generic RW sems. (reimplement) */
31#define USE_CRIT_SECT
32
33
34/*******************************************************************************
35* Header Files *
36*******************************************************************************/
37#include <iprt/semaphore.h>
38#include <iprt/alloc.h>
39#include <iprt/time.h>
40#include <iprt/asm.h>
41#include <iprt/assert.h>
42#include <iprt/thread.h>
43#include <iprt/err.h>
44#ifdef USE_CRIT_SECT
45#include <iprt/critsect.h>
46#endif
47
48
49
50/*******************************************************************************
51* Structures and Typedefs *
52*******************************************************************************/
53/** Internal representation of a Read-Write semaphore for the
54 * Generic implementation. */
55struct RTSEMRWINTERNAL
56{
57#ifdef USE_CRIT_SECT
58 /** Critical section. */
59 RTCRITSECT CritSect;
60#else
61 /** Magic (RTSEMRW_MAGIC). */
62 uint32_t u32Magic;
63 /** This critical section serializes the access to and updating of the structure members. */
64 RTCRITSECT CritSect;
65 /** The current number of readers. */
66 uint32_t cReaders;
67 /** The number of readers waiting. */
68 uint32_t cReadersWaiting;
69 /** The current number of waiting writers. */
70 uint32_t cWritersWaiting;
71 /** The handle of the event object on which the waiting readers block. (manual reset). */
72 RTSEMEVENTMULTI EventReaders;
73 /** The handle of the event object on which the waiting writers block. (manual reset). */
74 RTSEMEVENTMULTI EventWriters;
75 /** The current state of the read-write lock. */
76 KPRF_TYPE(,RWLOCKSTATE) enmState;
77
78#endif
79};
80
81
82/**
83 * Validate a read-write semaphore handle passed to one of the interface.
84 *
85 * @returns true if valid.
86 * @returns false if invalid.
87 * @param pIntRWSem Pointer to the read-write semaphore to validate.
88 */
89inline bool rtsemRWValid(struct RTSEMRWINTERNAL *pIntRWSem)
90{
91 if (!VALID_PTR(pIntRWSem))
92 return false;
93
94#ifdef USE_CRIT_SECT
95 if (pIntRWSem->CritSect.u32Magic != RTCRITSECT_MAGIC)
96 return false;
97#else
98 if (pIntRWSem->u32Check != (uint32_t)~0)
99 return false;
100#endif
101 return true;
102}
103
104
105RTDECL(int) RTSemRWCreate(PRTSEMRW pRWSem)
106{
107 int rc;
108
109 /*
110 * Allocate memory.
111 */
112 struct RTSEMRWINTERNAL *pIntRWSem = (struct RTSEMRWINTERNAL *)RTMemAlloc(sizeof(struct RTSEMRWINTERNAL));
113 if (pIntRWSem)
114 {
115#ifdef USE_CRIT_SECT
116 rc = RTCritSectInit(&pIntRWSem->CritSect);
117 if (RT_SUCCESS(rc))
118 {
119 *pRWSem = pIntRWSem;
120 return VINF_SUCCESS;
121 }
122#else
123 /*
124 * Create the semaphores.
125 */
126 rc = RTSemEventCreate(&pIntRWSem->WriteEvent);
127 if (RT_SUCCESS(rc))
128 {
129 rc = RTSemEventMultiCreate(&pIntRWSem->ReadEvent);
130 if (RT_SUCCESS(rc))
131 {
132 rc = RTSemMutexCreate(&pIntRWSem->Mutex);
133 if (RT_SUCCESS(rc))
134 {
135 /*
136 * Signal the read semaphore and initialize other variables.
137 */
138 rc = RTSemEventMultiSignal(pIntRWSem->ReadEvent);
139 if (RT_SUCCESS(rc))
140 {
141 pIntRWSem->cReaders = 0;
142 pIntRWSem->cWriters = 0;
143 pIntRWSem->WROwner = NIL_RTTHREAD;
144 pIntRWSem->u32Check = ~0;
145 *pRWSem = pIntRWSem;
146 return VINF_SUCCESS;
147 }
148 RTSemMutexDestroy(pIntRWSem->Mutex);
149 }
150 RTSemEventMultiDestroy(pIntRWSem->ReadEvent);
151 }
152 RTSemEventDestroy(pIntRWSem->WriteEvent);
153 }
154#endif
155 RTMemFree(pIntRWSem);
156 }
157 else
158 rc = VERR_NO_MEMORY;
159
160 return rc;
161}
162
163
164RTDECL(int) RTSemRWDestroy(RTSEMRW RWSem)
165{
166 /*
167 * Validate handle.
168 */
169 if (!rtsemRWValid(RWSem))
170 {
171 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
172 return VERR_INVALID_HANDLE;
173 }
174
175#ifdef USE_CRIT_SECT
176 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
177 int rc = RTCritSectDelete(&pIntRWSem->CritSect);
178 if (RT_SUCCESS(rc))
179 RTMemFree(pIntRWSem);
180 return rc;
181#else
182
183 /*
184 * Check if busy.
185 */
186 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
187 int rc = RTSemMutexRequest(pIntRWSem->Mutex, 32);
188 if (RT_SUCCESS(rc))
189 {
190 if (!pIntRWSem->cReaders && !pIntRWSem->cWriters)
191 {
192 /*
193 * Make it invalid and unusable.
194 */
195 ASMAtomicXchgU32(&pIntRWSem->u32Check, 0);
196 ASMAtomicXchgU32(&pIntRWSem->cReaders, ~0);
197
198 /*
199 * Do actual cleanup.
200 * None of these can now fail excep for the mutex which
201 * can be a little bit busy.
202 */
203 rc = RTSemEventMultiDestroy(pIntRWSem->ReadEvent);
204 AssertMsg(RT_SUCCESS(rc), ("RTSemEventMultiDestroy failed! rc=%d\n", rc)); NOREF(rc);
205 pIntRWSem->ReadEvent = NIL_RTSEMEVENTMULTI;
206
207 rc = RTSemEventDestroy(pIntRWSem->WriteEvent);
208 AssertMsg(RT_SUCCESS(rc), ("RTSemEventDestroy failed! rc=%d\n", rc)); NOREF(rc);
209 pIntRWSem->WriteEvent = NIL_RTSEMEVENT;
210
211 RTSemMutexRelease(pIntRWSem->Mutex);
212 for (unsigned i = 32; i > 0; i--)
213 {
214 rc = RTSemMutexDestroy(pIntRWSem->Mutex);
215 if (RT_SUCCESS(rc))
216 break;
217 RTThreadSleep(1);
218 }
219 AssertMsg(RT_SUCCESS(rc), ("RTSemMutexDestroy failed! rc=%d\n", rc)); NOREF(rc);
220 pIntRWSem->Mutex = (RTSEMMUTEX)0;
221
222 RTMemFree(pIntRWSem);
223 rc = VINF_SUCCESS;
224 }
225 else
226 {
227 rc = VERR_SEM_BUSY;
228 RTSemMutexRelease(pIntRWSem->Mutex);
229 }
230 }
231 else
232 rc = rc == VERR_TIMEOUT ? VERR_SEM_BUSY : rc;
233
234 return VINF_SUCCESS;
235#endif
236}
237
238
239RTDECL(int) RTSemRWRequestRead(RTSEMRW RWSem, unsigned cMillies)
240{
241 /*
242 * Validate handle.
243 */
244 if (!rtsemRWValid(RWSem))
245 {
246 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
247 return VERR_INVALID_HANDLE;
248 }
249
250#ifdef USE_CRIT_SECT
251 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
252 return RTCritSectEnter(&pIntRWSem->CritSect);
253#else
254
255 /*
256 * Take mutex and check if already reader.
257 */
258 //RTTHREAD Self = RTThreadSelf();
259 RTTHREAD Self = (RTTHREAD)RTThreadNativeSelf();
260 unsigned cMilliesInitial = cMillies;
261 uint64_t tsStart = 0;
262 if (cMillies != RTSEM_INDEFINITE_WAIT)
263 tsStart = RTTimeNanoTS();
264
265 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
266 int rc = RTSemMutexRequest(pIntRWSem->Mutex, RTSEM_INDEFINITE_WAIT);
267 if (RT_FAILURE(rc))
268 {
269 AssertMsgFailed(("RTSemMutexRequest failed on rwsem %p, rc=%d\n", RWSem, rc));
270 return rc;
271 }
272
273 unsigned i = pIntRWSem->cReaders;
274 while (i-- > 0)
275 {
276 if (pIntRWSem->aReaders[i].Thread == Self)
277 {
278 if (pIntRWSem->aReaders[i].cNesting + 1 < (unsigned)~0)
279 pIntRWSem->aReaders[i].cNesting++;
280 else
281 {
282 AssertMsgFailed(("Too many requests for one thread!\n"));
283 rc = RTSemMutexRelease(pIntRWSem->Mutex);
284 AssertMsg(RT_SUCCESS(rc), ("RTSemMutexRelease failed rc=%d\n", rc));
285 return VERR_TOO_MANY_SEM_REQUESTS;
286 }
287 }
288 }
289
290
291 for (;;)
292 {
293 /*
294 * Check if the stat of the affairs allow read access.
295 */
296 if (pIntRWSem->u32Check == (uint32_t)~0)
297 {
298 if (pIntRWSem->cWriters == 0)
299 {
300 if (pIntRWSem->cReaders < ELEMENTS(pIntRWSem->aReaders))
301 {
302 /*
303 * Add ourselves to the list of readers and return.
304 */
305 i = pIntRWSem->cReaders;
306 pIntRWSem->aReaders[i].Thread = Self;
307 pIntRWSem->aReaders[i].cNesting = 1;
308 ASMAtomicXchgU32(&pIntRWSem->cReaders, i + 1);
309
310 RTSemMutexRelease(pIntRWSem->Mutex);
311 return VINF_SUCCESS;
312 }
313 else
314 {
315 AssertMsgFailed(("Too many readers! How come we have so many threads!?!\n"));
316 rc = VERR_TOO_MANY_SEM_REQUESTS;
317 }
318 }
319 #if 0 /* any action here shouldn't be necessary */
320 else
321 {
322 rc = RTSemEventMultiReset(pIntRWSem->ReadEvent);
323 AssertMsg(RT_SUCCESS(rc), ("RTSemEventMultiReset failed on RWSem %p, rc=%d\n", RWSem, rc));
324 }
325 #endif
326 }
327 else
328 rc = VERR_SEM_DESTROYED;
329 RTSemMutexRelease(pIntRWSem->Mutex);
330 if (RT_FAILURE(rc))
331 break;
332
333
334 /*
335 * Wait till it's ready for reading.
336 */
337 if (cMillies != RTSEM_INDEFINITE_WAIT)
338 {
339 int64_t tsDelta = RTTimeNanoTS() - tsStart;
340 if (tsDelta >= 1000000)
341 {
342 cMillies = cMilliesInitial - (unsigned)(tsDelta / 1000000);
343 if (cMillies > cMilliesInitial)
344 cMillies = cMilliesInitial ? 1 : 0;
345 }
346 }
347 rc = RTSemEventMultiWait(pIntRWSem->ReadEvent, cMillies);
348 if (RT_FAILURE(rc))
349 {
350 AssertMsg(rc == VERR_TIMEOUT, ("RTSemEventMultiWait failed on rwsem %p, rc=%d\n", RWSem, rc));
351 break;
352 }
353
354 /*
355 * Get Mutex.
356 */
357 rc = RTSemMutexRequest(pIntRWSem->Mutex, RTSEM_INDEFINITE_WAIT);
358 if (RT_FAILURE(rc))
359 {
360 AssertMsgFailed(("RTSemMutexRequest failed on rwsem %p, rc=%d\n", RWSem, rc));
361 break;
362 }
363 }
364
365 return rc;
366#endif
367}
368
369
370RTDECL(int) RTSemRWRequestReadNoResume(RTSEMRW RWSem, unsigned cMillies)
371{
372 return RTSemRWRequestRead(RWSem, cMillies);
373}
374
375
376RTDECL(int) RTSemRWReleaseRead(RTSEMRW RWSem)
377{
378 /*
379 * Validate handle.
380 */
381 if (!rtsemRWValid(RWSem))
382 {
383 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
384 return VERR_INVALID_HANDLE;
385 }
386
387#ifdef USE_CRIT_SECT
388 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
389 return RTCritSectLeave(&pIntRWSem->CritSect);
390#else
391
392 /*
393 * Take Mutex.
394 */
395 //RTTHREAD Self = RTThreadSelf();
396 RTTHREAD Self = (RTTHREAD)RTThreadNativeSelf();
397 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
398 int rc = RTSemMutexRequest(pIntRWSem->Mutex, RTSEM_INDEFINITE_WAIT);
399 if (RT_SUCCESS(rc))
400 {
401 unsigned i = pIntRWSem->cReaders;
402 while (i-- > 0)
403 {
404 if (pIntRWSem->aReaders[i].Thread == Self)
405 {
406 AssertMsg(pIntRWSem->WROwner == NIL_RTTHREAD, ("Impossible! Writers and Readers are exclusive!\n"));
407
408 if (pIntRWSem->aReaders[i].cNesting <= 1)
409 {
410 pIntRWSem->aReaders[i] = pIntRWSem->aReaders[pIntRWSem->cReaders - 1];
411 ASMAtomicXchgU32(&pIntRWSem->cReaders, pIntRWSem->cReaders - 1);
412
413 /* Kick off writers? */
414 if ( pIntRWSem->cWriters > 0
415 && pIntRWSem->cReaders == 0)
416 {
417 rc = RTSemEventSignal(pIntRWSem->WriteEvent);
418 AssertMsg(RT_SUCCESS(rc), ("Failed to signal writers on rwsem %p, rc=%d\n", RWSem, rc));
419 }
420 }
421 else
422 pIntRWSem->aReaders[i].cNesting--;
423
424 RTSemMutexRelease(pIntRWSem->Mutex);
425 return VINF_SUCCESS;
426 }
427 }
428
429 RTSemMutexRelease(pIntRWSem->Mutex);
430 rc = VERR_NOT_OWNER;
431 AssertMsgFailed(("Not reader of rwsem %p\n", RWSem));
432 }
433 else
434 AssertMsgFailed(("RTSemMutexRequest failed on rwsem %p, rc=%d\n", RWSem, rc));
435
436 return rc;
437#endif
438}
439
440
441
442RTDECL(int) RTSemRWRequestWrite(RTSEMRW RWSem, unsigned cMillies)
443{
444 /*
445 * Validate handle.
446 */
447 if (!rtsemRWValid(RWSem))
448 {
449 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
450 return VERR_INVALID_HANDLE;
451 }
452
453#ifdef USE_CRIT_SECT
454 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
455 return RTCritSectEnter(&pIntRWSem->CritSect);
456#else
457
458 /*
459 * Get Mutex.
460 */
461 //RTTHREAD Self = RTThreadSelf();
462 RTTHREAD Self = (RTTHREAD)RTThreadNativeSelf();
463 unsigned cMilliesInitial = cMillies;
464 uint64_t tsStart = 0;
465 if (cMillies != RTSEM_INDEFINITE_WAIT)
466 tsStart = RTTimeNanoTS();
467
468 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
469 int rc = RTSemMutexRequest(pIntRWSem->Mutex, RTSEM_INDEFINITE_WAIT);
470 if (RT_FAILURE(rc))
471 {
472 AssertMsgFailed(("RTSemMutexWait failed on rwsem %p, rc=%d\n", RWSem, rc));
473 return rc;
474 }
475
476 /*
477 * Check that we're not a reader.
478 */
479 unsigned i = pIntRWSem->cReaders;
480 while (i-- > 0)
481 {
482 if (pIntRWSem->aReaders[i].Thread == Self)
483 {
484 AssertMsgFailed(("Deadlock - requested write access while being a reader! rwsem %p.\n", RWSem));
485 RTSemMutexRelease(pIntRWSem->Mutex);
486 return VERR_DEADLOCK;
487 }
488 }
489
490
491 /*
492 * Reset the reader event semaphore and increment number of readers.
493 */
494 rc = RTSemEventMultiReset(pIntRWSem->ReadEvent);
495 if (RT_FAILURE(rc))
496 {
497 AssertMsgFailed(("Failed to reset readers, rwsem %p, rc=%d.\n", RWSem, rc));
498 RTSemMutexRelease(pIntRWSem->Mutex);
499 return rc;
500 }
501 ASMAtomicXchgU32(&pIntRWSem->cWriters, pIntRWSem->cWriters + 1);
502
503 /*
504 * Wait while there are other threads owning this sem.
505 */
506 while ( pIntRWSem->WROwner != NIL_RTTHREAD
507 || pIntRWSem->cReaders > 0)
508 {
509 AssertMsg(pIntRWSem->WROwner == NIL_RTTHREAD || pIntRWSem->cWriters > 1,
510 ("The lock is write owned by there is only one waiter...\n"));
511
512 /*
513 * Release the mutex and wait on the writer semaphore.
514 */
515 rc = RTSemMutexRelease(pIntRWSem->Mutex);
516 if (RT_FAILURE(rc))
517 {
518 AssertMsgFailed(("RTSemMutexRelease failed on rwsem %p, rc=%d\n", RWSem, rc));
519 return VERR_SEM_DESTROYED;
520 }
521
522 /*
523 * Wait.
524 */
525 if (cMillies != RTSEM_INDEFINITE_WAIT)
526 {
527 int64_t tsDelta = RTTimeNanoTS() - tsStart;
528 if (tsDelta >= 1000000)
529 {
530 cMillies = cMilliesInitial - (unsigned)(tsDelta / 1000000);
531 if (cMillies > cMilliesInitial)
532 cMillies = cMilliesInitial ? 1 : 0;
533 }
534 }
535 rc = RTSemEventWait(pIntRWSem->WriteEvent, cMillies);
536
537 /*
538 * Check that the semaphore wasn't destroyed while we waited.
539 */
540 if ( rc == VERR_SEM_DESTROYED
541 || pIntRWSem->u32Check != (uint32_t)~0)
542 return VERR_SEM_DESTROYED;
543
544 /*
545 * Attempt take the mutex.
546 */
547 int rc2 = RTSemMutexRequest(pIntRWSem->Mutex, RTSEM_INDEFINITE_WAIT);
548 if (RT_FAILURE(rc) || RT_FAILURE(rc2))
549 {
550 AssertMsg(RT_SUCCESS(rc2), ("RTSemMutexRequest failed on rwsem %p, rc=%d\n", RWSem, rc2));
551 if (RT_SUCCESS(rc))
552 rc = rc2;
553 else
554 AssertMsg(rc == VERR_TIMEOUT, ("RTSemEventWait failed on rwsem %p, rc=%d\n", RWSem, rc));
555
556 /*
557 * Remove our selves from the writers queue.
558 */
559 /** @todo write an atomic dec function! (it's too late for that kind of stuff tonight) */
560 if (pIntRWSem->cWriters > 0)
561 ASMAtomicXchgU32(&pIntRWSem->cWriters, pIntRWSem->cWriters - 1);
562 if (!pIntRWSem->cWriters)
563 RTSemEventMultiSignal(pIntRWSem->ReadEvent);
564 if (RT_SUCCESS(rc2))
565 RTSemMutexRelease(pIntRWSem->Mutex);
566 return rc;
567 }
568
569 AssertMsg(pIntRWSem->WROwner == NIL_RTTHREAD, ("We woke up an there is owner! %#x\n", pIntRWSem->WROwner));
570 AssertMsg(!pIntRWSem->cReaders, ("We woke up an there are readers around!\n"));
571 }
572
573 /*
574 * If we get here we own the mutex and we are ready to take
575 * the read-write ownership.
576 */
577 ASMAtomicXchgPtr((void * volatile *)&pIntRWSem->WROwner, (void *)Self);
578 rc = RTSemMutexRelease(pIntRWSem->Mutex);
579 AssertMsg(RT_SUCCESS(rc), ("RTSemMutexRelease failed. rc=%d\n", rc)); NOREF(rc);
580
581 return VINF_SUCCESS;
582#endif
583}
584
585
586RTDECL(int) RTSemRWRequestWriteNoResume(RTSEMRW RWSem, unsigned cMillies)
587{
588 return RTSemRWRequestWrite(RWSem, cMillies);
589}
590
591
592
593RTDECL(int) RTSemRWReleaseWrite(RTSEMRW RWSem)
594{
595 /*
596 * Validate handle.
597 */
598 if (!rtsemRWValid(RWSem))
599 {
600 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
601 return VERR_INVALID_HANDLE;
602 }
603
604#ifdef USE_CRIT_SECT
605 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
606 return RTCritSectLeave(&pIntRWSem->CritSect);
607#else
608
609 /*
610 * Check if owner.
611 */
612 //RTTHREAD Self = RTThreadSelf();
613 RTTHREAD Self = (RTTHREAD)RTThreadNativeSelf();
614 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
615 if (pIntRWSem->WROwner != Self)
616 {
617 AssertMsgFailed(("Not read-write owner of rwsem %p.\n", RWSem));
618 return VERR_NOT_OWNER;
619 }
620
621 /*
622 * Request the mutex.
623 */
624 int rc = RTSemMutexRequest(pIntRWSem->Mutex, RTSEM_INDEFINITE_WAIT);
625 if (RT_FAILURE(rc))
626 {
627 AssertMsgFailed(("RTSemMutexWait failed on rwsem %p, rc=%d\n", RWSem, rc));
628 return rc;
629 }
630
631 /*
632 * Release ownership and remove ourselves from the writers count.
633 */
634 ASMAtomicXchgPtr((void * volatile *)&pIntRWSem->WROwner, (void *)NIL_RTTHREAD);
635 Assert(pIntRWSem->cWriters > 0);
636 ASMAtomicXchgU32(&pIntRWSem->cWriters, pIntRWSem->cWriters - 1);
637
638 /*
639 * Release the readers if no more writers.
640 */
641 if (!pIntRWSem->cWriters)
642 {
643 rc = RTSemEventMultiSignal(pIntRWSem->ReadEvent);
644 AssertMsg(RT_SUCCESS(rc), ("RTSemEventMultiSignal failed for rwsem %p, rc=%d.\n", RWSem, rc)); NOREF(rc);
645 }
646 rc = RTSemMutexRelease(pIntRWSem->Mutex);
647 AssertMsg(RT_SUCCESS(rc), ("RTSemEventMultiSignal failed for rwsem %p, rc=%d.\n", RWSem, rc)); NOREF(rc);
648
649 return VINF_SUCCESS;
650#endif
651}
652
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