VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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