VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/sems-linux.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: 26.8 KB
Line 
1/* $Id: sems-linux.cpp 5999 2007-12-07 15:05:06Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Semaphores, Linux (AMD64 only ATM).
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#include <iprt/semaphore.h>
31#include <iprt/assert.h>
32#include <iprt/alloc.h>
33#include <iprt/asm.h>
34#include <iprt/err.h>
35#include "internal/magics.h"
36
37#include <errno.h>
38#include <limits.h>
39#include <pthread.h>
40#include <unistd.h>
41#include <sys/time.h>
42#include <sys/syscall.h>
43#if 0 /* With 2.6.17 futex.h has become C++ unfriendly. */
44# include <linux/futex.h>
45#else
46# define FUTEX_WAIT 0
47# define FUTEX_WAKE 1
48#endif
49
50
51/*******************************************************************************
52* Structures and Typedefs *
53*******************************************************************************/
54
55/**
56 * Linux (single wakup) event semaphore.
57 */
58struct RTSEMEVENTINTERNAL
59{
60 /** Magic value. */
61 intptr_t volatile iMagic;
62 /** The futex state variable.
63 * <0 means signaled.
64 * 0 means not signaled, no waiters.
65 * >0 means not signaled, and the value gives the number of waiters.
66 */
67 int32_t volatile cWaiters;
68};
69
70
71/**
72 * Linux multiple wakup event semaphore.
73 */
74struct RTSEMEVENTMULTIINTERNAL
75{
76 /** Magic value. */
77 intptr_t volatile iMagic;
78 /** The futex state variable.
79 * -1 means signaled.
80 * 0 means not signaled, no waiters.
81 * >0 means not signaled, and the value gives the number of waiters.
82 */
83 int32_t volatile iState;
84};
85
86
87/**
88 * Posix internal representation of a Mutex semaphore.
89 */
90struct RTSEMMUTEXINTERNAL
91{
92 /** pthread mutex. */
93 pthread_mutex_t Mutex;
94 /** The owner of the mutex. */
95 volatile pthread_t Owner;
96 /** Nesting count. */
97 volatile uint32_t cNesting;
98};
99
100
101/**
102 * Posix internal representation of a read-write semaphore.
103 */
104struct RTSEMRWINTERNAL
105{
106 /** pthread rwlock. */
107 pthread_rwlock_t RWLock;
108 /** Variable to check if initialized.
109 * 0 is uninitialized, ~0 is inititialized. */
110 volatile unsigned uCheck;
111 /** The write owner of the lock. */
112 volatile pthread_t WROwner;
113};
114
115
116/**
117 * Wrapper for the futex syscall.
118 */
119static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
120{
121 errno = 0;
122 long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
123 if (rc < 0)
124 {
125 Assert(rc == -1);
126 rc = -errno;
127 }
128 return rc;
129}
130
131
132
133RTDECL(int) RTSemEventCreate(PRTSEMEVENT pEventSem)
134{
135 /*
136 * Allocate semaphore handle.
137 */
138 struct RTSEMEVENTINTERNAL *pIntEventSem = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTINTERNAL));
139 if (pIntEventSem)
140 {
141 pIntEventSem->iMagic = RTSEMEVENT_MAGIC;
142 pIntEventSem->cWaiters = 0;
143 *pEventSem = pIntEventSem;
144 return VINF_SUCCESS;
145 }
146 return VERR_NO_MEMORY;
147}
148
149
150RTDECL(int) RTSemEventDestroy(RTSEMEVENT EventSem)
151{
152 /*
153 * Validate input.
154 */
155 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
156 AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
157 VERR_INVALID_HANDLE);
158
159 /*
160 * Invalidate the semaphore and wake up anyone waiting on it.
161 */
162 ASMAtomicXchgSize(&pIntEventSem->iMagic, RTSEMEVENT_MAGIC + 1);
163 if (ASMAtomicXchgS32(&pIntEventSem->cWaiters, INT32_MIN / 2) > 0)
164 {
165 sys_futex(&pIntEventSem->cWaiters, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
166 usleep(1000);
167 }
168
169 /*
170 * Free the semaphore memory and be gone.
171 */
172 RTMemFree(pIntEventSem);
173 return VINF_SUCCESS;
174}
175
176
177RTDECL(int) RTSemEventSignal(RTSEMEVENT EventSem)
178{
179 /*
180 * Validate input.
181 */
182 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
183 AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
184 VERR_INVALID_HANDLE);
185 /*
186 * Try signal it.
187 */
188 for (unsigned i = 0;; i++)
189 {
190 int32_t iCur = pIntEventSem->cWaiters;
191 if (iCur == 0)
192 {
193 if (ASMAtomicCmpXchgS32(&pIntEventSem->cWaiters, -1, 0))
194 break; /* nobody is waiting */
195 }
196 else if (iCur < 0)
197 break; /* already signaled */
198 else
199 {
200 /* somebody is waiting, try wake up one of them. */
201 long cWoken = sys_futex(&pIntEventSem->cWaiters, FUTEX_WAKE, 1, NULL, NULL, 0);
202 if (RT_LIKELY(cWoken == 1))
203 {
204 ASMAtomicDecS32(&pIntEventSem->cWaiters);
205 break;
206 }
207 AssertMsg(cWoken == 0, ("%ld\n", cWoken));
208
209 /*
210 * This path is taken in two situations:
211 * 1) A waiting thread is returning from the sys_futex call with a
212 * non-zero return value.
213 * 2) There are two threads signaling the event at the
214 * same time and only one thread waiting.
215 *
216 * At this point we know that nobody is activly waiting on the event but
217 * at the same time, we are racing someone updating the state. The current
218 * strategy is to spin till the thread racing us is done, this is kind of
219 * brain dead and need fixing of course.
220 */
221 if (RT_UNLIKELY(i > 32))
222 {
223 if ((i % 128) == 127)
224 usleep(1000);
225 else if (!(i % 4))
226 pthread_yield();
227 else
228 AssertReleaseMsg(i < 4096, ("iCur=%#x pIntEventSem=%p\n", iCur, pIntEventSem));
229 }
230 }
231 }
232 return VINF_SUCCESS;
233}
234
235
236static int rtSemEventWait(RTSEMEVENT EventSem, unsigned cMillies, bool fAutoResume)
237{
238 /*
239 * Validate input.
240 */
241 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
242 AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
243 VERR_INVALID_HANDLE);
244
245 /*
246 * Quickly check whether it's signaled.
247 */
248 if (ASMAtomicCmpXchgS32(&pIntEventSem->cWaiters, 0, -1))
249 return VINF_SUCCESS;
250
251 /*
252 * Convert timeout value.
253 */
254 struct timespec ts;
255 struct timespec *pTimeout = 0;
256 if (cMillies != RT_INDEFINITE_WAIT)
257 {
258 ts.tv_sec = cMillies / 1000;
259 ts.tv_nsec = (cMillies % 1000) * 1000000;
260 pTimeout = &ts;
261 }
262
263 /*
264 * The wait loop.
265 */
266 for (unsigned i = 0;; i++)
267 {
268 /*
269 * Announce that we're among the waiters.
270 */
271 int32_t iNew = ASMAtomicIncS32(&pIntEventSem->cWaiters);
272 if (iNew == 0)
273 return VINF_SUCCESS;
274 if (RT_LIKELY(iNew > 0))
275 {
276 /*
277 * Go to sleep.
278 */
279 long rc = sys_futex(&pIntEventSem->cWaiters, FUTEX_WAIT, iNew, pTimeout, NULL, 0);
280 if (RT_UNLIKELY(pIntEventSem->iMagic != RTSEMEVENT_MAGIC))
281 return VERR_SEM_DESTROYED;
282
283 /* Did somebody wake us up us from RTSemEventSignal()? */
284 if (rc == 0)
285 return VINF_SUCCESS;
286
287 /* No, then the kernel woke us up or we failed going to sleep. Adjust the accounting. */
288 iNew = ASMAtomicDecS32(&pIntEventSem->cWaiters);
289 Assert(iNew >= 0);
290
291 /*
292 * Act on the wakup code.
293 */
294 if (rc == -ETIMEDOUT)
295 {
296 Assert(pTimeout);
297 return VERR_TIMEOUT;
298 }
299 if (rc == -EWOULDBLOCK)
300 /* retry with new value. */;
301 else if (rc == -EINTR)
302 {
303 if (!fAutoResume)
304 return VERR_INTERRUPTED;
305 }
306 else
307 {
308 /* this shouldn't happen! */
309 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
310 return RTErrConvertFromErrno(rc);
311 }
312 }
313 else
314 {
315 /* this can't happen. */
316 if (RT_UNLIKELY(pIntEventSem->iMagic != RTSEMEVENT_MAGIC))
317 return VERR_SEM_DESTROYED;
318 AssertReleaseMsgFailed(("iNew=%d\n", iNew));
319 }
320 }
321}
322
323
324RTDECL(int) RTSemEventWait(RTSEMEVENT EventSem, unsigned cMillies)
325{
326 int rc = rtSemEventWait(EventSem, cMillies, true);
327 Assert(rc != VERR_INTERRUPTED);
328 return rc;
329}
330
331
332RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT EventSem, unsigned cMillies)
333{
334 return rtSemEventWait(EventSem, cMillies, false);
335}
336
337
338
339
340
341RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI pEventMultiSem)
342{
343 /*
344 * Allocate semaphore handle.
345 */
346 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTMULTIINTERNAL));
347 if (pIntEventMultiSem)
348 {
349 pIntEventMultiSem->iMagic = RTSEMEVENTMULTI_MAGIC;
350 pIntEventMultiSem->iState = 0;
351 *pEventMultiSem = pIntEventMultiSem;
352 return VINF_SUCCESS;
353 }
354 return VERR_NO_MEMORY;
355}
356
357
358RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI EventMultiSem)
359{
360 /*
361 * Validate input.
362 */
363 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
364 AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
365 VERR_INVALID_HANDLE);
366
367 /*
368 * Invalidate the semaphore and wake up anyone waiting on it.
369 */
370 ASMAtomicXchgSize(&pIntEventMultiSem->iMagic, RTSEMEVENTMULTI_MAGIC + 1);
371 if (ASMAtomicXchgS32(&pIntEventMultiSem->iState, -1) == 1)
372 {
373 sys_futex(&pIntEventMultiSem->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
374 usleep(1000);
375 }
376
377 /*
378 * Free the semaphore memory and be gone.
379 */
380 RTMemFree(pIntEventMultiSem);
381 return VINF_SUCCESS;
382}
383
384
385RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI EventMultiSem)
386{
387 /*
388 * Validate input.
389 */
390 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
391 AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
392 VERR_INVALID_HANDLE);
393 /*
394 * Signal it.
395 */
396 int32_t iOld = ASMAtomicXchgS32(&pIntEventMultiSem->iState, -1);
397 if (iOld > 0)
398 {
399 /* wake up sleeping threads. */
400 long cWoken = sys_futex(&pIntEventMultiSem->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
401 AssertMsg(cWoken >= 0, ("%ld\n", cWoken)); NOREF(cWoken);
402 }
403 Assert(iOld == 0 || iOld == -1 || iOld == 1);
404 return VINF_SUCCESS;
405}
406
407
408RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI EventMultiSem)
409{
410 /*
411 * Validate input.
412 */
413 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
414 AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
415 VERR_INVALID_HANDLE);
416#ifdef RT_STRICT
417 int32_t i = pIntEventMultiSem->iState;
418 Assert(i == 0 || i == -1 || i == 1);
419#endif
420
421 /*
422 * Reset it.
423 */
424 ASMAtomicCmpXchgS32(&pIntEventMultiSem->iState, 0, -1);
425 return VINF_SUCCESS;
426}
427
428
429static int rtSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies, bool fAutoResume)
430{
431 /*
432 * Validate input.
433 */
434 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
435 AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
436 VERR_INVALID_HANDLE);
437
438 /*
439 * Quickly check whether it's signaled.
440 */
441 int32_t iCur = pIntEventMultiSem->iState;
442 Assert(iCur == 0 || iCur == -1 || iCur == 1);
443 if (iCur == -1)
444 return VINF_SUCCESS;
445 if (!cMillies)
446 return VERR_TIMEOUT;
447
448 /*
449 * Convert timeout value.
450 */
451 struct timespec ts;
452 struct timespec *pTimeout = NULL;
453 if (cMillies != RT_INDEFINITE_WAIT)
454 {
455 ts.tv_sec = cMillies / 1000;
456 ts.tv_nsec = (cMillies % 1000) * 1000000;
457 pTimeout = &ts;
458 }
459
460 /*
461 * The wait loop.
462 */
463 for (unsigned i = 0;; i++)
464 {
465 /*
466 * Start waiting. We only account for there being or having been
467 * threads waiting on the semaphore to keep things simple.
468 */
469 iCur = pIntEventMultiSem->iState;
470 Assert(iCur == 0 || iCur == -1 || iCur == 1);
471 if ( iCur == 1
472 || ASMAtomicCmpXchgS32(&pIntEventMultiSem->iState, 1, 0))
473 {
474 long rc = sys_futex(&pIntEventMultiSem->iState, FUTEX_WAIT, 1, pTimeout, NULL, 0);
475 if (RT_UNLIKELY(pIntEventMultiSem->iMagic != RTSEMEVENTMULTI_MAGIC))
476 return VERR_SEM_DESTROYED;
477 if (rc == 0)
478 return VINF_SUCCESS;
479
480 /*
481 * Act on the wakup code.
482 */
483 if (rc == -ETIMEDOUT)
484 {
485 Assert(pTimeout);
486 return VERR_TIMEOUT;
487 }
488 if (rc == -EWOULDBLOCK)
489 /* retry, the value changed. */;
490 else if (rc == -EINTR)
491 {
492 if (!fAutoResume)
493 return VERR_INTERRUPTED;
494 }
495 else
496 {
497 /* this shouldn't happen! */
498 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
499 return RTErrConvertFromErrno(rc);
500 }
501 }
502 else if (iCur == -1)
503 return VINF_SUCCESS;
504 }
505}
506
507
508RTDECL(int) RTSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
509{
510 int rc = rtSemEventMultiWait(EventMultiSem, cMillies, true);
511 Assert(rc != VERR_INTERRUPTED);
512 return rc;
513}
514
515
516RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
517{
518 return rtSemEventMultiWait(EventMultiSem, cMillies, false);
519}
520
521
522
523
524
525/**
526 * Validate a Mutex semaphore handle passed to one of the interface.
527 *
528 * @returns true if valid.
529 * @returns false if invalid.
530 * @param pIntMutexSem Pointer to the mutex semaphore to validate.
531 */
532inline bool rtsemMutexValid(struct RTSEMMUTEXINTERNAL *pIntMutexSem)
533{
534 if ((uintptr_t)pIntMutexSem < 0x10000)
535 return false;
536
537 if (pIntMutexSem->cNesting == (uint32_t)~0)
538 return false;
539
540 return true;
541}
542
543
544RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX pMutexSem)
545{
546 int rc;
547
548 /*
549 * Allocate semaphore handle.
550 */
551 struct RTSEMMUTEXINTERNAL *pIntMutexSem = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
552 if (pIntMutexSem)
553 {
554 /*
555 * Create the semaphore.
556 */
557 pthread_mutexattr_t MutexAttr;
558 rc = pthread_mutexattr_init(&MutexAttr);
559 if (!rc)
560 {
561 rc = pthread_mutex_init(&pIntMutexSem->Mutex, &MutexAttr);
562 if (!rc)
563 {
564 pthread_mutexattr_destroy(&MutexAttr);
565
566 pIntMutexSem->Owner = (pthread_t)~0;
567 pIntMutexSem->cNesting = 0;
568
569 *pMutexSem = pIntMutexSem;
570 return VINF_SUCCESS;
571 }
572 pthread_mutexattr_destroy(&MutexAttr);
573 }
574 RTMemFree(pIntMutexSem);
575 }
576 else
577 rc = VERR_NO_MEMORY;
578
579 return rc;
580}
581
582
583RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX MutexSem)
584{
585 /*
586 * Validate input.
587 */
588 if (!rtsemMutexValid(MutexSem))
589 {
590 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
591 return VERR_INVALID_HANDLE;
592 }
593
594 /*
595 * Try destroy it.
596 */
597 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
598 int rc = pthread_mutex_destroy(&pIntMutexSem->Mutex);
599 if (rc)
600 {
601 AssertMsgFailed(("Failed to destroy mutex sem %p, rc=%d.\n", MutexSem, rc));
602 return RTErrConvertFromErrno(rc);
603 }
604
605 /*
606 * Free the memory and be gone.
607 */
608 pIntMutexSem->Owner = (pthread_t)~0;
609 pIntMutexSem->cNesting = ~0;
610 RTMemTmpFree(pIntMutexSem);
611
612 return VINF_SUCCESS;
613}
614
615
616RTDECL(int) RTSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies)
617{
618 /*
619 * Validate input.
620 */
621 if (!rtsemMutexValid(MutexSem))
622 {
623 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
624 return VERR_INVALID_HANDLE;
625 }
626
627 /*
628 * Check if nested request.
629 */
630 pthread_t Self = pthread_self();
631 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
632 if ( pIntMutexSem->Owner == Self
633 && pIntMutexSem->cNesting > 0)
634 {
635 pIntMutexSem->cNesting++;
636 return VINF_SUCCESS;
637 }
638
639 /*
640 * Lock it.
641 */
642 if (cMillies == RT_INDEFINITE_WAIT)
643 {
644 /* take mutex */
645 int rc = pthread_mutex_lock(&pIntMutexSem->Mutex);
646 if (rc)
647 {
648 AssertMsgFailed(("Failed to lock mutex sem %p, rc=%d.\n", MutexSem, rc)); NOREF(rc);
649 return RTErrConvertFromErrno(rc);
650 }
651 }
652 else
653 {
654 /*
655 * Get current time and calc end of wait time.
656 */
657 struct timespec ts = {0,0};
658 clock_gettime(CLOCK_REALTIME, &ts);
659 if (cMillies != 0)
660 {
661 ts.tv_nsec += (cMillies % 1000) * 1000000;
662 ts.tv_sec += cMillies / 1000;
663 if (ts.tv_nsec >= 1000000000)
664 {
665 ts.tv_nsec -= 1000000000;
666 ts.tv_sec++;
667 }
668 }
669
670 /* take mutex */
671 int rc = pthread_mutex_timedlock(&pIntMutexSem->Mutex, &ts);
672 if (rc)
673 {
674 AssertMsg(rc == ETIMEDOUT, ("Failed to lock mutex sem %p, rc=%d.\n", MutexSem, rc)); NOREF(rc);
675 return RTErrConvertFromErrno(rc);
676 }
677 }
678
679 /*
680 * Set the owner and nesting.
681 */
682 pIntMutexSem->Owner = Self;
683 ASMAtomicXchgU32(&pIntMutexSem->cNesting, 1);
684
685 return VINF_SUCCESS;
686}
687
688
689RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX MutexSem, unsigned cMillies)
690{
691 /* EINTR isn't returned by the wait functions we're using. */
692 return RTSemMutexRequest(MutexSem, cMillies);
693}
694
695
696RTDECL(int) RTSemMutexRelease(RTSEMMUTEX MutexSem)
697{
698 /*
699 * Validate input.
700 */
701 if (!rtsemMutexValid(MutexSem))
702 {
703 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
704 return VERR_INVALID_HANDLE;
705 }
706
707 /*
708 * Check if nested.
709 */
710 pthread_t Self = pthread_self();
711 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
712 if ( pIntMutexSem->Owner != Self
713 || pIntMutexSem->cNesting == (uint32_t)~0)
714 {
715 AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
716 pIntMutexSem, Self, pIntMutexSem->Owner, pIntMutexSem->cNesting));
717 return VERR_NOT_OWNER;
718 }
719
720 /*
721 * If nested we'll just pop a nesting.
722 */
723 if (pIntMutexSem->cNesting > 1)
724 {
725 pIntMutexSem->cNesting--;
726 return VINF_SUCCESS;
727 }
728
729 /*
730 * Clear the state. (cNesting == 1)
731 */
732 pIntMutexSem->Owner = (pthread_t)~0;
733 ASMAtomicXchgU32(&pIntMutexSem->cNesting, 0);
734
735 /*
736 * Unlock mutex semaphore.
737 */
738 int rc = pthread_mutex_unlock(&pIntMutexSem->Mutex);
739 if (rc)
740 {
741 AssertMsgFailed(("Failed to unlock mutex sem %p, rc=%d.\n", MutexSem, rc)); NOREF(rc);
742 return RTErrConvertFromErrno(rc);
743 }
744
745 return VINF_SUCCESS;
746}
747
748
749
750
751
752/**
753 * Validate a read-write semaphore handle passed to one of the interface.
754 *
755 * @returns true if valid.
756 * @returns false if invalid.
757 * @param pIntRWSem Pointer to the read-write semaphore to validate.
758 */
759inline bool rtsemRWValid(struct RTSEMRWINTERNAL *pIntRWSem)
760{
761 if ((uintptr_t)pIntRWSem < 0x10000)
762 return false;
763
764 if (pIntRWSem->uCheck != (unsigned)~0)
765 return false;
766
767 return true;
768}
769
770
771RTDECL(int) RTSemRWCreate(PRTSEMRW pRWSem)
772{
773 int rc;
774
775 /*
776 * Allocate handle.
777 */
778 struct RTSEMRWINTERNAL *pIntRWSem = (struct RTSEMRWINTERNAL *)RTMemAlloc(sizeof(struct RTSEMRWINTERNAL));
779 if (pIntRWSem)
780 {
781 /*
782 * Create the rwlock.
783 */
784 pthread_rwlockattr_t Attr;
785 rc = pthread_rwlockattr_init(&Attr);
786 if (!rc)
787 {
788 rc = pthread_rwlock_init(&pIntRWSem->RWLock, &Attr);
789 if (!rc)
790 {
791 pIntRWSem->uCheck = ~0;
792 pIntRWSem->WROwner = (pthread_t)~0;
793 *pRWSem = pIntRWSem;
794 return VINF_SUCCESS;
795 }
796 }
797
798 rc = RTErrConvertFromErrno(rc);
799 RTMemFree(pIntRWSem);
800 }
801 else
802 rc = VERR_NO_MEMORY;
803
804 return rc;
805}
806
807
808RTDECL(int) RTSemRWDestroy(RTSEMRW RWSem)
809{
810 /*
811 * Validate input.
812 */
813 if (!rtsemRWValid(RWSem))
814 {
815 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
816 return VERR_INVALID_HANDLE;
817 }
818
819 /*
820 * Try destroy it.
821 */
822 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
823 int rc = pthread_rwlock_destroy(&pIntRWSem->RWLock);
824 if (!rc)
825 {
826 pIntRWSem->uCheck = 0;
827 RTMemFree(pIntRWSem);
828 rc = VINF_SUCCESS;
829 }
830 else
831 {
832 AssertMsgFailed(("Failed to destroy read-write sem %p, rc=%d.\n", RWSem, rc));
833 rc = RTErrConvertFromErrno(rc);
834 }
835
836 return rc;
837}
838
839
840RTDECL(int) RTSemRWRequestRead(RTSEMRW RWSem, unsigned cMillies)
841{
842 /*
843 * Validate input.
844 */
845 if (!rtsemRWValid(RWSem))
846 {
847 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
848 return VERR_INVALID_HANDLE;
849 }
850
851 /*
852 * Try lock it.
853 */
854 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
855 if (cMillies == RT_INDEFINITE_WAIT)
856 {
857 /* take rwlock */
858 int rc = pthread_rwlock_rdlock(&pIntRWSem->RWLock);
859 if (rc)
860 {
861 AssertMsgFailed(("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
862 return RTErrConvertFromErrno(rc);
863 }
864 }
865 else
866 {
867 /*
868 * Get current time and calc end of wait time.
869 */
870 struct timespec ts = {0,0};
871 clock_gettime(CLOCK_REALTIME, &ts);
872 if (cMillies != 0)
873 {
874 ts.tv_nsec += (cMillies % 1000) * 1000000;
875 ts.tv_sec += cMillies / 1000;
876 if (ts.tv_nsec >= 1000000000)
877 {
878 ts.tv_nsec -= 1000000000;
879 ts.tv_sec++;
880 }
881 }
882
883 /* take rwlock */
884 int rc = pthread_rwlock_timedrdlock(&pIntRWSem->RWLock, &ts);
885 if (rc)
886 {
887 AssertMsg(rc == ETIMEDOUT, ("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
888 return RTErrConvertFromErrno(rc);
889 }
890 }
891
892 return VINF_SUCCESS;
893}
894
895
896RTDECL(int) RTSemRWRequestReadNoResume(RTSEMRW RWSem, unsigned cMillies)
897{
898 /* EINTR isn't returned by the wait functions we're using. */
899 return RTSemRWRequestRead(RWSem, cMillies);
900}
901
902
903RTDECL(int) RTSemRWReleaseRead(RTSEMRW RWSem)
904{
905 /*
906 * Validate input.
907 */
908 if (!rtsemRWValid(RWSem))
909 {
910 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
911 return VERR_INVALID_HANDLE;
912 }
913
914 /*
915 * Try unlock it.
916 */
917 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
918 if (pIntRWSem->WROwner == pthread_self())
919 {
920 AssertMsgFailed(("Tried to read unlock when write owner - read-write sem %p.\n", RWSem));
921 return VERR_NOT_OWNER;
922 }
923 int rc = pthread_rwlock_unlock(&pIntRWSem->RWLock);
924 if (rc)
925 {
926 AssertMsgFailed(("Failed read unlock read-write sem %p, rc=%d.\n", RWSem, rc));
927 return RTErrConvertFromErrno(rc);
928 }
929
930 return VINF_SUCCESS;
931}
932
933
934RTDECL(int) RTSemRWRequestWrite(RTSEMRW RWSem, unsigned cMillies)
935{
936 /*
937 * Validate input.
938 */
939 if (!rtsemRWValid(RWSem))
940 {
941 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
942 return VERR_INVALID_HANDLE;
943 }
944
945 /*
946 * Try lock it.
947 */
948 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
949 if (cMillies == RT_INDEFINITE_WAIT)
950 {
951 /* take rwlock */
952 int rc = pthread_rwlock_wrlock(&pIntRWSem->RWLock);
953 if (rc)
954 {
955 AssertMsgFailed(("Failed write lock read-write sem %p, rc=%d.\n", RWSem, rc));
956 return RTErrConvertFromErrno(rc);
957 }
958 }
959 else
960 {
961 /*
962 * Get current time and calc end of wait time.
963 */
964 struct timespec ts = {0,0};
965 clock_gettime(CLOCK_REALTIME, &ts);
966 if (cMillies != 0)
967 {
968 ts.tv_nsec += (cMillies % 1000) * 1000000;
969 ts.tv_sec += cMillies / 1000;
970 if (ts.tv_nsec >= 1000000000)
971 {
972 ts.tv_nsec -= 1000000000;
973 ts.tv_sec++;
974 }
975 }
976
977 /* take rwlock */
978 int rc = pthread_rwlock_timedwrlock(&pIntRWSem->RWLock, &ts);
979 if (rc)
980 {
981 AssertMsg(rc == ETIMEDOUT, ("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
982 return RTErrConvertFromErrno(rc);
983 }
984 }
985
986 ASMAtomicXchgPtr((void * volatile *)&pIntRWSem->WROwner, (void *)pthread_self());
987
988 return VINF_SUCCESS;
989}
990
991
992RTDECL(int) RTSemRWRequestWriteNoResume(RTSEMRW RWSem, unsigned cMillies)
993{
994 /* EINTR isn't returned by the wait functions we're using. */
995 return RTSemRWRequestWrite(RWSem, cMillies);
996}
997
998
999RTDECL(int) RTSemRWReleaseWrite(RTSEMRW RWSem)
1000{
1001 /*
1002 * Validate input.
1003 */
1004 if (!rtsemRWValid(RWSem))
1005 {
1006 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
1007 return VERR_INVALID_HANDLE;
1008 }
1009
1010 /*
1011 * Try unlock it.
1012 */
1013 pthread_t Self = pthread_self();
1014 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1015 if (pIntRWSem->WROwner != Self)
1016 {
1017 AssertMsgFailed(("Not Write owner!\n"));
1018 return VERR_NOT_OWNER;
1019 }
1020
1021 /*
1022 * Try unlock it.
1023 */
1024 AssertMsg(sizeof(pthread_t) == sizeof(void *), ("pthread_t is not the size of a pointer but %d bytes\n", sizeof(pthread_t)));
1025 ASMAtomicXchgPtr((void * volatile *)&pIntRWSem->WROwner, (void *)(uintptr_t)~0);
1026 int rc = pthread_rwlock_unlock(&pIntRWSem->RWLock);
1027 if (rc)
1028 {
1029 AssertMsgFailed(("Failed write unlock read-write sem %p, rc=%d.\n", RWSem, rc));
1030 return RTErrConvertFromErrno(rc);
1031 }
1032
1033 return VINF_SUCCESS;
1034}
1035
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette