VirtualBox

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

Last change on this file since 1804 was 849, checked in by vboxsync, 18 years ago

In 2.6.17 linux/futex.h starts including linux/sched.h and is no longer usable from C++ code. Sigh.

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