VirtualBox

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

Last change on this file since 25046 was 21337, checked in by vboxsync, 15 years ago

IPRT,HostDrv,AddDrv: Export public IPRT symbols for the linux kernel (pain).

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