VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/sems-linux.cpp@ 4071

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