VirtualBox

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

Last change on this file since 3991 was 2981, checked in by vboxsync, 17 years ago

InnoTek -> innotek: all the headers and comments.

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