VirtualBox

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

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

Runtime/generic SemRW: fix return values

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.9 KB
Line 
1/* $Id: semsrw-generic.cpp 20603 2009-06-15 19:14:51Z 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/*******************************************************************************
37* Header Files *
38*******************************************************************************/
39#include <iprt/semaphore.h>
40#include <iprt/critsect.h>
41#include <iprt/alloc.h>
42#include <iprt/time.h>
43#include <iprt/asm.h>
44#include <iprt/assert.h>
45#include <iprt/thread.h>
46#include <iprt/err.h>
47#include <iprt/stream.h>
48
49#include "internal/magics.h"
50
51
52/*******************************************************************************
53* Structures and Typedefs *
54*******************************************************************************/
55
56/** Internal representation of a Read-Write semaphore for the
57 * Generic implementation. */
58struct RTSEMRWINTERNAL
59{
60 /** The usual magic. (RTSEMRW_MAGIC) */
61 uint32_t u32Magic;
62 /* Alignment padding. */
63 uint32_t u32Padding;
64 /** This critical section serializes the access to and updating of the structure members. */
65 RTCRITSECT CritSect;
66 /** The current number of reads. (pure read recursion counts too) */
67 uint32_t cReads;
68 /** The current number of writes. (recursion counts too) */
69 uint32_t cWrites;
70 /** Number of read recursions by the writer. */
71 uint32_t cWriterReads;
72 /** Number of writers waiting. */
73 uint32_t cWritesWaiting;
74 /** The write owner of the lock. */
75 RTTHREAD Writer;
76 /** The handle of the event object on which the waiting readers block. (manual reset). */
77 RTSEMEVENTMULTI ReadEvent;
78 /** The handle of the event object on which the waiting writers block. (automatic reset). */
79 RTSEMEVENT WriteEvent;
80};
81
82
83/**
84 * Validate a read-write semaphore handle passed to one of the interface.
85 *
86 * @returns true if valid.
87 * @returns false if invalid.
88 * @param pThis Pointer to the read-write semaphore to validate.
89 */
90inline bool rtsemRWValid(struct RTSEMRWINTERNAL *pThis)
91{
92 if (!VALID_PTR(pThis))
93 return false;
94
95 if (pThis->u32Magic != RTSEMRW_MAGIC)
96 return false;
97 return true;
98}
99
100
101RTDECL(int) RTSemRWCreate(PRTSEMRW pRWSem)
102{
103 int rc;
104
105 /*
106 * Allocate memory.
107 */
108 struct RTSEMRWINTERNAL *pThis = (struct RTSEMRWINTERNAL *)RTMemAlloc(sizeof(struct RTSEMRWINTERNAL));
109 if (pThis)
110 {
111 /*
112 * Create the semaphores.
113 */
114 rc = RTSemEventCreate(&pThis->WriteEvent);
115 if (RT_SUCCESS(rc))
116 {
117 rc = RTSemEventMultiCreate(&pThis->ReadEvent);
118 if (RT_SUCCESS(rc))
119 {
120 rc = RTCritSectInit(&pThis->CritSect);
121 if (RT_SUCCESS(rc))
122 {
123 /*
124 * Signal the read semaphore and initialize other variables.
125 */
126 rc = RTSemEventMultiSignal(pThis->ReadEvent);
127 if (RT_SUCCESS(rc))
128 {
129 pThis->u32Padding = 0xa5a55a5a;
130 pThis->cReads = 0;
131 pThis->cWrites = 0;
132 pThis->cWriterReads = 0;
133 pThis->cWritesWaiting = 0;
134 pThis->Writer = NIL_RTTHREAD;
135 pThis->u32Magic = RTSEMRW_MAGIC;
136 *pRWSem = pThis;
137 return VINF_SUCCESS;
138 }
139 RTCritSectDelete(&pThis->CritSect);
140 }
141 RTSemEventMultiDestroy(pThis->ReadEvent);
142 }
143 RTSemEventDestroy(pThis->WriteEvent);
144 }
145 RTMemFree(pThis);
146 }
147 else
148 rc = VERR_NO_MEMORY;
149
150 return rc;
151}
152
153
154RTDECL(int) RTSemRWDestroy(RTSEMRW RWSem)
155{
156 struct RTSEMRWINTERNAL *pThis = RWSem;
157 /*
158 * Validate handle.
159 */
160 if (!rtsemRWValid(pThis))
161 {
162 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
163 return VERR_INVALID_HANDLE;
164 }
165
166 /*
167 * Check if busy.
168 */
169 int rc = RTCritSectTryEnter(&pThis->CritSect);
170 if (RT_SUCCESS(rc))
171 {
172 if (!pThis->cReads && !pThis->cWrites)
173 {
174 /*
175 * Make it invalid and unusable.
176 */
177 pThis->u32Magic = ~RTSEMRW_MAGIC;
178 pThis->cReads = ~0;
179
180 /*
181 * Do actual cleanup. None of these can now fail.
182 */
183 rc = RTSemEventMultiDestroy(pThis->ReadEvent);
184 AssertMsgRC(rc, ("RTSemEventMultiDestroy failed! rc=%d\n", rc));
185 pThis->ReadEvent = NIL_RTSEMEVENTMULTI;
186
187 rc = RTSemEventDestroy(pThis->WriteEvent);
188 AssertMsgRC(rc, ("RTSemEventDestroy failed! rc=%d\n", rc));
189 pThis->WriteEvent = NIL_RTSEMEVENT;
190
191 RTCritSectLeave(&pThis->CritSect);
192 rc = RTCritSectDelete(&pThis->CritSect);
193 AssertMsgRC(rc, ("RTCritSectDelete failed! rc=%d\n", rc));
194
195 RTMemFree(pThis);
196 rc = VINF_SUCCESS;
197 }
198 else
199 {
200 rc = VERR_SEM_BUSY;
201 RTCritSectLeave(&pThis->CritSect);
202 }
203 }
204 else
205 {
206 AssertMsgRC(rc, ("RTCritSectTryEnter failed! rc=%d\n", rc));
207 rc = VERR_SEM_BUSY;
208 }
209
210 return rc;
211}
212
213
214RTDECL(int) RTSemRWRequestRead(RTSEMRW RWSem, unsigned cMillies)
215{
216 struct RTSEMRWINTERNAL *pThis = RWSem;
217 /*
218 * Validate handle.
219 */
220 if (!rtsemRWValid(pThis))
221 {
222 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
223 return VERR_INVALID_HANDLE;
224 }
225
226 RTTHREAD Self = (RTTHREAD)RTThreadNativeSelf();
227 unsigned cMilliesInitial = cMillies;
228 uint64_t tsStart = 0;
229 if (cMillies != RT_INDEFINITE_WAIT)
230 tsStart = RTTimeNanoTS();
231
232 /*
233 * Take critsect.
234 */
235 int rc = RTCritSectEnter(&pThis->CritSect);
236 if (RT_FAILURE(rc))
237 {
238 AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%d\n", RWSem, rc));
239 return rc;
240 }
241
242 for (;;)
243 {
244 /*
245 * Check if the state of affairs allows read access.
246 * Do not block further readers if there is a writer waiting, as
247 * that will break/deadlock reader recursion.
248 */
249 if (!pThis->cWrites)
250 {
251 pThis->cReads++;
252
253 RTCritSectLeave(&pThis->CritSect);
254 return VINF_SUCCESS;
255 }
256 else if (pThis->Writer == Self)
257 {
258 pThis->cWriterReads++;
259
260 RTCritSectLeave(&pThis->CritSect);
261 return VINF_SUCCESS;
262 }
263
264 RTCritSectLeave(&pThis->CritSect);
265
266 /*
267 * Wait till it's ready for reading.
268 */
269 if (cMillies != RT_INDEFINITE_WAIT)
270 {
271 int64_t tsDelta = RTTimeNanoTS() - tsStart;
272 if (tsDelta >= 1000000)
273 {
274 cMillies = cMilliesInitial - (unsigned)(tsDelta / 1000000);
275 if (cMillies > cMilliesInitial)
276 cMillies = cMilliesInitial ? 1 : 0;
277 }
278 }
279 rc = RTSemEventMultiWait(pThis->ReadEvent, cMillies);
280 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
281 {
282 AssertMsgRC(rc, ("RTSemEventMultiWait failed on rwsem %p, rc=%d\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.
294 */
295 rc = RTCritSectEnter(&pThis->CritSect);
296 if (RT_FAILURE(rc))
297 {
298 AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%d\n", RWSem, rc));
299 break;
300 }
301 }
302
303 return rc;
304}
305
306
307RTDECL(int) RTSemRWRequestReadNoResume(RTSEMRW RWSem, unsigned cMillies)
308{
309 return RTSemRWRequestRead(RWSem, cMillies);
310}
311
312
313RTDECL(int) RTSemRWReleaseRead(RTSEMRW RWSem)
314{
315 struct RTSEMRWINTERNAL *pThis = RWSem;
316 /*
317 * Validate handle.
318 */
319 if (!rtsemRWValid(pThis))
320 {
321 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
322 return VERR_INVALID_HANDLE;
323 }
324
325 RTTHREAD Self = (RTTHREAD)RTThreadNativeSelf();
326
327 /*
328 * Take critsect.
329 */
330 int rc = RTCritSectEnter(&pThis->CritSect);
331 if (RT_SUCCESS(rc))
332 {
333 if (pThis->Writer == Self)
334 {
335 pThis->cWriterReads--;
336 }
337 else
338 {
339 AssertMsg(pThis->Writer == NIL_RTTHREAD, ("Impossible! Writers and Readers are exclusive!\n"));
340 pThis->cReads--;
341
342 /* Kick off a writer if appropriate. */
343 if ( pThis->cWritesWaiting > 0
344 && !pThis->cReads)
345 {
346 rc = RTSemEventSignal(pThis->WriteEvent);
347 AssertMsgRC(rc, ("Failed to signal writers on rwsem %p, rc=%d\n", RWSem, rc));
348 }
349 }
350
351 RTCritSectLeave(&pThis->CritSect);
352 return VINF_SUCCESS;
353 }
354 else
355 AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%d\n", RWSem, rc));
356
357 return rc;
358}
359
360
361RTDECL(int) RTSemRWRequestWrite(RTSEMRW RWSem, unsigned cMillies)
362{
363 struct RTSEMRWINTERNAL *pThis = RWSem;
364 /*
365 * Validate handle.
366 */
367 if (!rtsemRWValid(pThis))
368 {
369 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
370 return VERR_INVALID_HANDLE;
371 }
372
373 RTTHREAD Self = (RTTHREAD)RTThreadNativeSelf();
374 unsigned cMilliesInitial = cMillies;
375 uint64_t tsStart = 0;
376 if (cMillies != RT_INDEFINITE_WAIT)
377 tsStart = RTTimeNanoTS();
378
379 /*
380 * Take critsect.
381 */
382 int rc = RTCritSectEnter(&pThis->CritSect);
383 if (RT_FAILURE(rc))
384 {
385 AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%d\n", RWSem, rc));
386 return rc;
387 }
388
389 /*
390 * Signal writer presence.
391 */
392 pThis->cWritesWaiting++;
393
394 for (;;)
395 {
396 /*
397 * Check if the state of affairs allows write access.
398 */
399 if (!pThis->cReads && (!pThis->cWrites || pThis->Writer == Self))
400 {
401 /*
402 * Reset the reader event semaphore. For write recursion this
403 * is redundant, but does not hurt.
404 */
405 rc = RTSemEventMultiReset(pThis->ReadEvent);
406 AssertMsgRC(rc, ("Failed to reset readers, rwsem %p, rc=%d.\n", RWSem, rc));
407
408 pThis->cWrites++;
409 pThis->Writer = Self;
410 /* We're not waiting, so decrease counter. */
411 pThis->cWritesWaiting--;
412 RTCritSectLeave(&pThis->CritSect);
413 return VINF_SUCCESS;
414 }
415
416 RTCritSectLeave(&pThis->CritSect);
417
418 /*
419 * Wait till it's ready for writing.
420 */
421 if (cMillies != RT_INDEFINITE_WAIT)
422 {
423 int64_t tsDelta = RTTimeNanoTS() - tsStart;
424 if (tsDelta >= 1000000)
425 {
426 cMillies = cMilliesInitial - (unsigned)(tsDelta / 1000000);
427 if (cMillies > cMilliesInitial)
428 cMillies = cMilliesInitial ? 1 : 0;
429 }
430 }
431 rc = RTSemEventWait(pThis->WriteEvent, cMillies);
432 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
433 {
434 AssertMsgRC(rc, ("RTSemEventWait failed on rwsem %p, rc=%d\n", RWSem, rc));
435 break;
436 }
437
438 if (pThis->u32Magic != RTSEMRW_MAGIC)
439 {
440 rc = VERR_SEM_DESTROYED;
441 break;
442 }
443
444 /*
445 * Re-take critsect.
446 */
447 rc = RTCritSectEnter(&pThis->CritSect);
448 if (RT_FAILURE(rc))
449 {
450 AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%d\n", RWSem, rc));
451 break;
452 }
453// AssertMsg(!pThis->cReads, ("We woke up and there are readers around!\n"));
454 }
455
456 /*
457 * Timeout/error case, clean up.
458 */
459 if (pThis->u32Magic == RTSEMRW_MAGIC)
460 {
461 RTCritSectEnter(&pThis->CritSect);
462 /* Adjust this counter, whether we got the critsect or not. */
463 pThis->cWritesWaiting--;
464 RTCritSectLeave(&pThis->CritSect);
465 }
466 return rc;
467}
468
469
470RTDECL(int) RTSemRWRequestWriteNoResume(RTSEMRW RWSem, unsigned cMillies)
471{
472 return RTSemRWRequestWrite(RWSem, cMillies);
473}
474
475
476RTDECL(int) RTSemRWReleaseWrite(RTSEMRW RWSem)
477{
478 struct RTSEMRWINTERNAL *pThis = RWSem;
479 /*
480 * Validate handle.
481 */
482 if (!rtsemRWValid(pThis))
483 {
484 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
485 return VERR_INVALID_HANDLE;
486 }
487
488 RTTHREAD Self = (RTTHREAD)RTThreadNativeSelf();
489
490 /*
491 * Take critsect.
492 */
493 int rc = RTCritSectEnter(&pThis->CritSect);
494 if (RT_FAILURE(rc))
495 {
496 AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%d\n", RWSem, rc));
497 return rc;
498 }
499
500 /*
501 * Check if owner.
502 */
503 if (pThis->Writer != Self)
504 {
505 RTCritSectLeave(&pThis->CritSect);
506 AssertMsgFailed(("Not read-write owner of rwsem %p.\n", RWSem));
507 return VERR_NOT_OWNER;
508 }
509
510 Assert(pThis->cWrites > 0);
511 /*
512 * Release ownership and remove ourselves from the writers count.
513 */
514 pThis->cWrites--;
515 if (!pThis->cWrites)
516 pThis->Writer = NIL_RTTHREAD;
517
518 /*
519 * Release the readers if no more writers waiting, otherwise the writers.
520 */
521 if (!pThis->cWritesWaiting)
522 {
523 rc = RTSemEventMultiSignal(pThis->ReadEvent);
524 AssertMsgRC(rc, ("RTSemEventMultiSignal failed for rwsem %p, rc=%d.\n", RWSem, rc));
525 }
526 else
527 {
528 rc = RTSemEventSignal(pThis->WriteEvent);
529 AssertMsgRC(rc, ("Failed to signal writers on rwsem %p, rc=%d\n", RWSem, rc));
530 }
531 RTCritSectLeave(&pThis->CritSect);
532
533 return rc;
534}
535
536
537RTDECL(bool) RTSemRWIsWriteOwner(RTSEMRW RWSem)
538{
539 struct RTSEMRWINTERNAL *pThis = RWSem;
540 /*
541 * Validate handle.
542 */
543 if (!rtsemRWValid(pThis))
544 {
545 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
546 return false;
547 }
548
549 /*
550 * Check ownership.
551 */
552 RTTHREAD Self = (RTTHREAD)RTThreadNativeSelf();
553 RTTHREAD Writer;
554 ASMAtomicUoReadSize(&pThis->Writer, &Writer);
555 return Writer == Self;
556}
557
558
559RTDECL(uint32_t) RTSemRWGetWriteRecursion(RTSEMRW RWSem)
560{
561 struct RTSEMRWINTERNAL *pThis = RWSem;
562 /*
563 * Validate handle.
564 */
565 if (!rtsemRWValid(pThis))
566 {
567 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
568 return 0;
569 }
570
571 /*
572 * Return the requested data.
573 */
574 return pThis->cWrites;
575}
576
577
578RTDECL(uint32_t) RTSemRWGetWriterReadRecursion(RTSEMRW RWSem)
579{
580 struct RTSEMRWINTERNAL *pThis = RWSem;
581 /*
582 * Validate handle.
583 */
584 if (!rtsemRWValid(pThis))
585 {
586 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
587 return 0;
588 }
589
590 /*
591 * Return the requested data.
592 */
593 return pThis->cWriterReads;
594}
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