VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/semrw-generic.cpp@ 25524

Last change on this file since 25524 was 25522, checked in by vboxsync, 15 years ago

semrw-generic.cpp: Fixed ownership checks in the ReleaseRead code path. Optimized out almost all the RTThreadNativeSelf calls and fixed all the native/iprt thread type mixup.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.8 KB
Line 
1/* $Id: semrw-generic.cpp 25522 2009-12-20 16:45:08Z vboxsync $ */
2/** @file
3 * IPRT - 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-2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
30 * Clara, CA 95054 USA or visit http://www.sun.com if you need
31 * additional information or have any questions.
32 */
33
34
35/*******************************************************************************
36* Header Files *
37*******************************************************************************/
38#include <iprt/semaphore.h>
39#include "internal/iprt.h"
40
41#include <iprt/critsect.h>
42#include <iprt/alloc.h>
43#include <iprt/time.h>
44#include <iprt/asm.h>
45#include <iprt/assert.h>
46#include <iprt/thread.h>
47#include <iprt/err.h>
48#include <iprt/stream.h>
49
50#include "internal/magics.h"
51
52
53/*******************************************************************************
54* Structures and Typedefs *
55*******************************************************************************/
56
57/** Internal representation of a Read-Write semaphore for the
58 * Generic implementation. */
59struct RTSEMRWINTERNAL
60{
61 /** The usual magic. (RTSEMRW_MAGIC) */
62 uint32_t u32Magic;
63 /* Alignment padding. */
64 uint32_t u32Padding;
65 /** This critical section serializes the access to and updating of the structure members. */
66 RTCRITSECT CritSect;
67 /** The current number of reads. (pure read recursion counts too) */
68 uint32_t cReads;
69 /** The current number of writes. (recursion counts too) */
70 uint32_t cWrites;
71 /** Number of read recursions by the writer. */
72 uint32_t cWriterReads;
73 /** Number of writers waiting. */
74 uint32_t cWritesWaiting;
75 /** The write owner of the lock. */
76 RTNATIVETHREAD hWriter;
77 /** The handle of the event object on which the waiting readers block. (manual reset). */
78 RTSEMEVENTMULTI ReadEvent;
79 /** The handle of the event object on which the waiting writers block. (automatic reset). */
80 RTSEMEVENT WriteEvent;
81 /** Need to reset ReadEvent. */
82 bool fNeedResetReadEvent;
83};
84
85
86
87RTDECL(int) RTSemRWCreate(PRTSEMRW pRWSem)
88{
89 int rc;
90
91 /*
92 * Allocate memory.
93 */
94 struct RTSEMRWINTERNAL *pThis = (struct RTSEMRWINTERNAL *)RTMemAlloc(sizeof(struct RTSEMRWINTERNAL));
95 if (pThis)
96 {
97 /*
98 * Create the semaphores.
99 */
100 rc = RTSemEventCreate(&pThis->WriteEvent);
101 if (RT_SUCCESS(rc))
102 {
103 rc = RTSemEventMultiCreate(&pThis->ReadEvent);
104 if (RT_SUCCESS(rc))
105 {
106 rc = RTCritSectInit(&pThis->CritSect);
107 if (RT_SUCCESS(rc))
108 {
109 /*
110 * Signal the read semaphore and initialize other variables.
111 */
112 rc = RTSemEventMultiSignal(pThis->ReadEvent);
113 if (RT_SUCCESS(rc))
114 {
115 pThis->u32Padding = UINT32_C(0xa5a55a5a);
116 pThis->cReads = 0;
117 pThis->cWrites = 0;
118 pThis->cWriterReads = 0;
119 pThis->cWritesWaiting = 0;
120 pThis->hWriter = NIL_RTNATIVETHREAD;
121 pThis->fNeedResetReadEvent = true;
122 pThis->u32Magic = RTSEMRW_MAGIC;
123 *pRWSem = pThis;
124 return VINF_SUCCESS;
125 }
126 RTCritSectDelete(&pThis->CritSect);
127 }
128 RTSemEventMultiDestroy(pThis->ReadEvent);
129 }
130 RTSemEventDestroy(pThis->WriteEvent);
131 }
132 RTMemFree(pThis);
133 }
134 else
135 rc = VERR_NO_MEMORY;
136
137 return rc;
138}
139RT_EXPORT_SYMBOL(RTSemRWCreate);
140
141
142RTDECL(int) RTSemRWDestroy(RTSEMRW RWSem)
143{
144 struct RTSEMRWINTERNAL *pThis = RWSem;
145
146 /*
147 * Validate handle.
148 */
149 if (pThis == NIL_RTSEMRW)
150 return VINF_SUCCESS;
151 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
152 AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
153
154 /*
155 * Check if busy.
156 */
157 int rc = RTCritSectTryEnter(&pThis->CritSect);
158 if (RT_SUCCESS(rc))
159 {
160 if (!pThis->cReads && !pThis->cWrites)
161 {
162 /*
163 * Make it invalid and unusable.
164 */
165 ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMRW_MAGIC);
166 pThis->cReads = ~0;
167
168 /*
169 * Do actual cleanup. None of these can now fail.
170 */
171 rc = RTSemEventMultiDestroy(pThis->ReadEvent);
172 AssertMsgRC(rc, ("RTSemEventMultiDestroy failed! rc=%Rrc\n", rc));
173 pThis->ReadEvent = NIL_RTSEMEVENTMULTI;
174
175 rc = RTSemEventDestroy(pThis->WriteEvent);
176 AssertMsgRC(rc, ("RTSemEventDestroy failed! rc=%Rrc\n", rc));
177 pThis->WriteEvent = NIL_RTSEMEVENT;
178
179 RTCritSectLeave(&pThis->CritSect);
180 rc = RTCritSectDelete(&pThis->CritSect);
181 AssertMsgRC(rc, ("RTCritSectDelete failed! rc=%Rrc\n", rc));
182
183 RTMemFree(pThis);
184 rc = VINF_SUCCESS;
185 }
186 else
187 {
188 rc = VERR_SEM_BUSY;
189 RTCritSectLeave(&pThis->CritSect);
190 }
191 }
192 else
193 {
194 AssertMsgRC(rc, ("RTCritSectTryEnter failed! rc=%Rrc\n", rc));
195 rc = VERR_SEM_BUSY;
196 }
197
198 return rc;
199}
200RT_EXPORT_SYMBOL(RTSemRWDestroy);
201
202
203RTDECL(int) RTSemRWRequestRead(RTSEMRW RWSem, unsigned cMillies)
204{
205 struct RTSEMRWINTERNAL *pThis = RWSem;
206
207 /*
208 * Validate handle.
209 */
210 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
211 AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
212
213 unsigned cMilliesInitial = cMillies;
214 uint64_t tsStart = 0;
215 if (cMillies != RT_INDEFINITE_WAIT && cMillies != 0)
216 tsStart = RTTimeNanoTS();
217
218 /*
219 * Take critsect.
220 */
221 int rc = RTCritSectEnter(&pThis->CritSect);
222 if (RT_FAILURE(rc))
223 {
224 AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%Rrc\n", RWSem, rc));
225 return rc;
226 }
227
228 /*
229 * Check if the state of affairs allows read access.
230 * Do not block further readers if there is a writer waiting, as
231 * that will break/deadlock reader recursion.
232 */
233 if ( pThis->hWriter == NIL_RTNATIVETHREAD
234#if 0
235 && ( !pThis->cWritesWaiting
236 || pThis->cReads)
237#endif
238 )
239 {
240 pThis->cReads++;
241 Assert(pThis->cReads > 0);
242
243 RTCritSectLeave(&pThis->CritSect);
244 return VINF_SUCCESS;
245 }
246
247 RTNATIVETHREAD hNativeSelf = pThis->CritSect.NativeThreadOwner;
248 if (pThis->hWriter == hNativeSelf)
249 {
250 pThis->cWriterReads++;
251 Assert(pThis->cWriterReads > 0);
252
253 RTCritSectLeave(&pThis->CritSect);
254 return VINF_SUCCESS;
255 }
256
257 RTCritSectLeave(&pThis->CritSect);
258
259 /*
260 * Wait till it's ready for reading.
261 */
262 if (cMillies == 0)
263 return VERR_TIMEOUT;
264
265 for (;;)
266 {
267 if (cMillies != RT_INDEFINITE_WAIT)
268 {
269 int64_t tsDelta = RTTimeNanoTS() - tsStart;
270 if (tsDelta >= 1000000)
271 {
272 tsDelta /= 1000000;
273 if ((uint64_t)tsDelta < cMilliesInitial)
274 cMilliesInitial = (unsigned)tsDelta;
275 else
276 cMilliesInitial = 1;
277 }
278 }
279 int rcWait = rc = RTSemEventMultiWait(pThis->ReadEvent, cMillies);
280 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT) /* handle timeout below */
281 {
282 AssertMsgRC(rc, ("RTSemEventMultiWait failed on rwsem %p, rc=%Rrc\n", RWSem, rc));
283 break;
284 }
285
286 if (pThis->u32Magic != RTSEMRW_MAGIC)
287 {
288 rc = VERR_SEM_DESTROYED;
289 break;
290 }
291
292 /*
293 * Re-take critsect and repeate the check we did before the loop.
294 */
295 rc = RTCritSectEnter(&pThis->CritSect);
296 if (RT_FAILURE(rc))
297 {
298 AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%Rrc\n", RWSem, rc));
299 break;
300 }
301
302 if ( pThis->hWriter == NIL_RTNATIVETHREAD
303#if 0
304 && ( !pThis->cWritesWaiting
305 || pThis->cReads)
306#endif
307 )
308 {
309 pThis->cReads++;
310
311 RTCritSectLeave(&pThis->CritSect);
312 return VINF_SUCCESS;
313 }
314
315 RTCritSectLeave(&pThis->CritSect);
316
317 /*
318 * Quit if the wait already timed out.
319 */
320 if (rcWait == VERR_TIMEOUT)
321 {
322 rc = VERR_TIMEOUT;
323 break;
324 }
325 }
326
327 /* failed */
328 return rc;
329}
330RT_EXPORT_SYMBOL(RTSemRWRequestRead);
331
332
333RTDECL(int) RTSemRWRequestReadNoResume(RTSEMRW RWSem, unsigned cMillies)
334{
335 return RTSemRWRequestRead(RWSem, cMillies);
336}
337RT_EXPORT_SYMBOL(RTSemRWRequestReadNoResume);
338
339
340RTDECL(int) RTSemRWReleaseRead(RTSEMRW RWSem)
341{
342 struct RTSEMRWINTERNAL *pThis = RWSem;
343
344 /*
345 * Validate handle.
346 */
347 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
348 AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
349
350 /*
351 * Take critsect.
352 */
353 int rc = RTCritSectEnter(&pThis->CritSect);
354 if (RT_SUCCESS(rc))
355 {
356 if (pThis->hWriter == NIL_RTNATIVETHREAD)
357 {
358 if (RT_LIKELY(pThis->cReads > 0))
359 {
360 pThis->cReads--;
361
362 /* Kick off a writer if appropriate. */
363 if ( pThis->cWritesWaiting > 0
364 && !pThis->cReads)
365 {
366 rc = RTSemEventSignal(pThis->WriteEvent);
367 AssertMsgRC(rc, ("Failed to signal writers on rwsem %p, rc=%Rrc\n", RWSem, rc));
368 }
369 }
370 else
371 {
372 AssertFailed();
373 rc = VERR_NOT_OWNER;
374 }
375 }
376 else
377 {
378 RTNATIVETHREAD hNativeSelf = pThis->CritSect.NativeThreadOwner;
379 if (pThis->hWriter == hNativeSelf)
380 {
381 if (pThis->cWriterReads > 0)
382 pThis->cWriterReads--;
383 else
384 {
385 AssertFailed();
386 rc = VERR_NOT_OWNER;
387 }
388 }
389 else
390 {
391 AssertFailed();
392 rc = VERR_NOT_OWNER;
393 }
394 }
395
396 RTCritSectLeave(&pThis->CritSect);
397 }
398 else
399 AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%Rrc\n", RWSem, rc));
400
401 return rc;
402}
403RT_EXPORT_SYMBOL(RTSemRWReleaseRead);
404
405
406RTDECL(int) RTSemRWRequestWrite(RTSEMRW RWSem, unsigned cMillies)
407{
408 struct RTSEMRWINTERNAL *pThis = RWSem;
409
410 /*
411 * Validate handle.
412 */
413 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
414 AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
415
416 unsigned cMilliesInitial = cMillies;
417 uint64_t tsStart = 0;
418 if (cMillies != RT_INDEFINITE_WAIT && cMillies != 0)
419 tsStart = RTTimeNanoTS();
420
421 /*
422 * Take critsect.
423 */
424 int rc = RTCritSectEnter(&pThis->CritSect);
425 if (RT_FAILURE(rc))
426 {
427 AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%Rrc\n", RWSem, rc));
428 return rc;
429 }
430
431 /*
432 * Check if the state of affairs allows write access.
433 */
434 RTNATIVETHREAD hNativeSelf = pThis->CritSect.NativeThreadOwner;
435 if (!pThis->cReads && (!pThis->cWrites || pThis->hWriter == hNativeSelf))
436 {
437 /*
438 * Reset the reader event semaphore if necessary.
439 */
440 if (pThis->fNeedResetReadEvent)
441 {
442 pThis->fNeedResetReadEvent = false;
443 rc = RTSemEventMultiReset(pThis->ReadEvent);
444 AssertMsgRC(rc, ("Failed to reset readers, rwsem %p, rc=%Rrc.\n", RWSem, rc));
445 }
446
447 pThis->cWrites++;
448 pThis->hWriter = hNativeSelf;
449
450 RTCritSectLeave(&pThis->CritSect);
451 return VINF_SUCCESS;
452 }
453
454 /*
455 * Signal writer presence.
456 */
457 if (cMillies != 0)
458 pThis->cWritesWaiting++;
459
460 RTCritSectLeave(&pThis->CritSect);
461
462 /*
463 * Wait till it's ready for writing.
464 */
465 if (cMillies == 0)
466 return VERR_TIMEOUT;
467
468 for (;;)
469 {
470 if (cMillies != RT_INDEFINITE_WAIT)
471 {
472 int64_t tsDelta = RTTimeNanoTS() - tsStart;
473 if (tsDelta >= 1000000)
474 {
475 tsDelta /= 1000000;
476 if ((uint64_t)tsDelta < cMilliesInitial)
477 cMilliesInitial = (unsigned)tsDelta;
478 else
479 cMilliesInitial = 1;
480 }
481 }
482 int rcWait = rc = RTSemEventWait(pThis->WriteEvent, cMillies);
483 if (RT_UNLIKELY(RT_FAILURE_NP(rc) && rc != VERR_TIMEOUT)) /* timeouts are handled below */
484 {
485 AssertMsgRC(rc, ("RTSemEventWait failed on rwsem %p, rc=%Rrc\n", RWSem, rc));
486 break;
487 }
488
489 if (RT_UNLIKELY(pThis->u32Magic != RTSEMRW_MAGIC))
490 {
491 rc = VERR_SEM_DESTROYED;
492 break;
493 }
494
495 /*
496 * Re-take critsect and repeate the check we did prior to this loop.
497 */
498 rc = RTCritSectEnter(&pThis->CritSect);
499 if (RT_FAILURE(rc))
500 {
501 AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%Rrc\n", RWSem, rc));
502 break;
503 }
504
505 if (!pThis->cReads && (!pThis->cWrites || pThis->hWriter == hNativeSelf))
506 {
507 /*
508 * Reset the reader event semaphore if necessary.
509 */
510 if (pThis->fNeedResetReadEvent)
511 {
512 pThis->fNeedResetReadEvent = false;
513 rc = RTSemEventMultiReset(pThis->ReadEvent);
514 AssertMsgRC(rc, ("Failed to reset readers, rwsem %p, rc=%Rrc.\n", RWSem, rc));
515 }
516
517 pThis->cWrites++;
518 pThis->hWriter = hNativeSelf;
519 pThis->cWritesWaiting--;
520
521 RTCritSectLeave(&pThis->CritSect);
522 return VINF_SUCCESS;
523 }
524
525 RTCritSectLeave(&pThis->CritSect);
526
527 /*
528 * Quit if the wait already timed out.
529 */
530 if (rcWait == VERR_TIMEOUT)
531 {
532 rc = VERR_TIMEOUT;
533 break;
534 }
535 }
536
537 /*
538 * Timeout/error case, clean up.
539 */
540 if (pThis->u32Magic == RTSEMRW_MAGIC)
541 {
542 RTCritSectEnter(&pThis->CritSect);
543 /* Adjust this counter, whether we got the critsect or not. */
544 pThis->cWritesWaiting--;
545 RTCritSectLeave(&pThis->CritSect);
546 }
547 return rc;
548}
549RT_EXPORT_SYMBOL(RTSemRWRequestWrite);
550
551
552RTDECL(int) RTSemRWRequestWriteNoResume(RTSEMRW RWSem, unsigned cMillies)
553{
554 return RTSemRWRequestWrite(RWSem, cMillies);
555}
556RT_EXPORT_SYMBOL(RTSemRWRequestWriteNoResume);
557
558
559RTDECL(int) RTSemRWReleaseWrite(RTSEMRW RWSem)
560{
561 struct RTSEMRWINTERNAL *pThis = RWSem;
562
563 /*
564 * Validate handle.
565 */
566 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
567 AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
568
569 /*
570 * Take critsect.
571 */
572 int rc = RTCritSectEnter(&pThis->CritSect);
573 AssertRCReturn(rc, rc);
574
575 /*
576 * Check if owner.
577 */
578 RTNATIVETHREAD hNativeSelf = pThis->CritSect.NativeThreadOwner;
579 if (pThis->hWriter != hNativeSelf)
580 {
581 RTCritSectLeave(&pThis->CritSect);
582 AssertMsgFailed(("Not read-write owner of rwsem %p.\n", RWSem));
583 return VERR_NOT_OWNER;
584 }
585
586 /*
587 * Release ownership and remove ourselves from the writers count.
588 */
589 Assert(pThis->cWrites > 0);
590 pThis->cWrites--;
591 if (!pThis->cWrites)
592 {
593 if (RT_UNLIKELY(pThis->cWriterReads > 0))
594 {
595 pThis->cWrites++;
596 RTCritSectLeave(&pThis->CritSect);
597 AssertMsgFailed(("All recursive read locks need to be released prior to the final write lock! (%p)n\n", pThis));
598 return VERR_WRONG_ORDER;
599 }
600
601 pThis->hWriter = NIL_RTNATIVETHREAD;
602 }
603
604 /*
605 * Release the readers if no more writers waiting, otherwise the writers.
606 */
607 if (!pThis->cWritesWaiting)
608 {
609 rc = RTSemEventMultiSignal(pThis->ReadEvent);
610 AssertMsgRC(rc, ("RTSemEventMultiSignal failed for rwsem %p, rc=%Rrc.\n", RWSem, rc));
611 pThis->fNeedResetReadEvent = true;
612 }
613 else
614 {
615 rc = RTSemEventSignal(pThis->WriteEvent);
616 AssertMsgRC(rc, ("Failed to signal writers on rwsem %p, rc=%Rrc\n", RWSem, rc));
617 }
618 RTCritSectLeave(&pThis->CritSect);
619
620 return rc;
621}
622RT_EXPORT_SYMBOL(RTSemRWReleaseWrite);
623
624
625RTDECL(bool) RTSemRWIsWriteOwner(RTSEMRW RWSem)
626{
627 struct RTSEMRWINTERNAL *pThis = RWSem;
628
629 /*
630 * Validate handle.
631 */
632 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
633 AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
634
635 /*
636 * Check ownership.
637 */
638 RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
639 RTNATIVETHREAD hWriter;
640 ASMAtomicUoReadHandle(&pThis->hWriter, &hWriter);
641 return hWriter == hNativeSelf;
642}
643RT_EXPORT_SYMBOL(RTSemRWIsWriteOwner);
644
645
646RTDECL(uint32_t) RTSemRWGetWriteRecursion(RTSEMRW RWSem)
647{
648 struct RTSEMRWINTERNAL *pThis = RWSem;
649
650 /*
651 * Validate handle.
652 */
653 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
654 AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
655
656 /*
657 * Return the requested data.
658 */
659 return pThis->cWrites;
660}
661RT_EXPORT_SYMBOL(RTSemRWGetWriteRecursion);
662
663
664RTDECL(uint32_t) RTSemRWGetWriterReadRecursion(RTSEMRW RWSem)
665{
666 struct RTSEMRWINTERNAL *pThis = RWSem;
667
668 /*
669 * Validate handle.
670 */
671 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
672 AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
673
674 /*
675 * Return the requested data.
676 */
677 return pThis->cWriterReads;
678}
679RT_EXPORT_SYMBOL(RTSemRWGetWriterReadRecursion);
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