VirtualBox

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

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

Fix bug in mutex semaphore reimplementation without pthreads. The releasing of the semaphore was incorrectly using non-atomic operations. Also removed the unneeded setting of the mutex state on timeout, and told the compiler about how to optimize the control flow. Still disabled, as there is no testcase.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 32.8 KB
Line 
1/* $Id: sems-linux.cpp 6731 2008-02-01 19:34:27Z 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#ifndef VBOX_REWRITTEN_MUTEX
88/**
89 * Posix internal representation of a Mutex semaphore.
90 */
91struct RTSEMMUTEXINTERNAL
92{
93 /** pthread mutex. */
94 pthread_mutex_t Mutex;
95 /** The owner of the mutex. */
96 volatile pthread_t Owner;
97 /** Nesting count. */
98 volatile uint32_t cNesting;
99};
100#else /* VBOX_REWRITTEN_MUTEX */
101/**
102 * Linux internal representation of a Mutex semaphore.
103 */
104struct RTSEMMUTEXINTERNAL
105{
106 /** Magic value. */
107 intptr_t volatile iMagic;
108 /** The futex state variable.
109 * 0 means unlocked.
110 * 1 means locked, no waiters.
111 * 2 means locked, one or more waiters.
112 */
113 int32_t volatile iState;
114 /** The owner of the mutex. */
115 volatile pthread_t Owner;
116 /** Nesting count. */
117 volatile uint32_t cNesting;
118};
119#endif /* VBOX_REWRITTEN_MUTEX */
120
121
122/**
123 * Posix internal representation of a read-write semaphore.
124 */
125struct RTSEMRWINTERNAL
126{
127 /** pthread rwlock. */
128 pthread_rwlock_t RWLock;
129 /** Variable to check if initialized.
130 * 0 is uninitialized, ~0 is inititialized. */
131 volatile unsigned uCheck;
132 /** The write owner of the lock. */
133 volatile pthread_t WROwner;
134};
135
136
137/**
138 * Wrapper for the futex syscall.
139 */
140static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
141{
142 errno = 0;
143 long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
144 if (rc < 0)
145 {
146 Assert(rc == -1);
147 rc = -errno;
148 }
149 return rc;
150}
151
152
153
154RTDECL(int) RTSemEventCreate(PRTSEMEVENT pEventSem)
155{
156 /*
157 * Allocate semaphore handle.
158 */
159 struct RTSEMEVENTINTERNAL *pIntEventSem = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTINTERNAL));
160 if (pIntEventSem)
161 {
162 pIntEventSem->iMagic = RTSEMEVENT_MAGIC;
163 pIntEventSem->cWaiters = 0;
164 *pEventSem = pIntEventSem;
165 return VINF_SUCCESS;
166 }
167 return VERR_NO_MEMORY;
168}
169
170
171RTDECL(int) RTSemEventDestroy(RTSEMEVENT EventSem)
172{
173 /*
174 * Validate input.
175 */
176 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
177 AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
178 VERR_INVALID_HANDLE);
179
180 /*
181 * Invalidate the semaphore and wake up anyone waiting on it.
182 */
183 ASMAtomicXchgSize(&pIntEventSem->iMagic, RTSEMEVENT_MAGIC + 1);
184 if (ASMAtomicXchgS32(&pIntEventSem->cWaiters, INT32_MIN / 2) > 0)
185 {
186 sys_futex(&pIntEventSem->cWaiters, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
187 usleep(1000);
188 }
189
190 /*
191 * Free the semaphore memory and be gone.
192 */
193 RTMemFree(pIntEventSem);
194 return VINF_SUCCESS;
195}
196
197
198RTDECL(int) RTSemEventSignal(RTSEMEVENT EventSem)
199{
200 /*
201 * Validate input.
202 */
203 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
204 AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
205 VERR_INVALID_HANDLE);
206 /*
207 * Try signal it.
208 */
209 for (unsigned i = 0;; i++)
210 {
211 int32_t iCur = pIntEventSem->cWaiters;
212 if (iCur == 0)
213 {
214 if (ASMAtomicCmpXchgS32(&pIntEventSem->cWaiters, -1, 0))
215 break; /* nobody is waiting */
216 }
217 else if (iCur < 0)
218 break; /* already signaled */
219 else
220 {
221 /* somebody is waiting, try wake up one of them. */
222 long cWoken = sys_futex(&pIntEventSem->cWaiters, FUTEX_WAKE, 1, NULL, NULL, 0);
223 if (RT_LIKELY(cWoken == 1))
224 {
225 ASMAtomicDecS32(&pIntEventSem->cWaiters);
226 break;
227 }
228 AssertMsg(cWoken == 0, ("%ld\n", cWoken));
229
230 /*
231 * This path is taken in two situations:
232 * 1) A waiting thread is returning from the sys_futex call with a
233 * non-zero return value.
234 * 2) There are two threads signaling the event at the
235 * same time and only one thread waiting.
236 *
237 * At this point we know that nobody is activly waiting on the event but
238 * at the same time, we are racing someone updating the state. The current
239 * strategy is to spin till the thread racing us is done, this is kind of
240 * brain dead and need fixing of course.
241 */
242 if (RT_UNLIKELY(i > 32))
243 {
244 if ((i % 128) == 127)
245 usleep(1000);
246 else if (!(i % 4))
247 pthread_yield();
248 else
249 AssertReleaseMsg(i < 4096, ("iCur=%#x pIntEventSem=%p\n", iCur, pIntEventSem));
250 }
251 }
252 }
253 return VINF_SUCCESS;
254}
255
256
257static int rtSemEventWait(RTSEMEVENT EventSem, unsigned cMillies, bool fAutoResume)
258{
259 /*
260 * Validate input.
261 */
262 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
263 AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
264 VERR_INVALID_HANDLE);
265
266 /*
267 * Quickly check whether it's signaled.
268 */
269 if (ASMAtomicCmpXchgS32(&pIntEventSem->cWaiters, 0, -1))
270 return VINF_SUCCESS;
271
272 /*
273 * Convert timeout value.
274 */
275 struct timespec ts;
276 struct timespec *pTimeout = 0;
277 if (cMillies != RT_INDEFINITE_WAIT)
278 {
279 ts.tv_sec = cMillies / 1000;
280 ts.tv_nsec = (cMillies % 1000) * 1000000;
281 pTimeout = &ts;
282 }
283
284 /*
285 * The wait loop.
286 */
287 for (unsigned i = 0;; i++)
288 {
289 /*
290 * Announce that we're among the waiters.
291 */
292 int32_t iNew = ASMAtomicIncS32(&pIntEventSem->cWaiters);
293 if (iNew == 0)
294 return VINF_SUCCESS;
295 if (RT_LIKELY(iNew > 0))
296 {
297 /*
298 * Go to sleep.
299 */
300 long rc = sys_futex(&pIntEventSem->cWaiters, FUTEX_WAIT, iNew, pTimeout, NULL, 0);
301 if (RT_UNLIKELY(pIntEventSem->iMagic != RTSEMEVENT_MAGIC))
302 return VERR_SEM_DESTROYED;
303
304 /* Did somebody wake us up from RTSemEventSignal()? */
305 if (rc == 0)
306 return VINF_SUCCESS;
307
308 /* No, then the kernel woke us up or we failed going to sleep. Adjust the accounting. */
309 iNew = ASMAtomicDecS32(&pIntEventSem->cWaiters);
310 Assert(iNew >= 0);
311
312 /*
313 * Act on the wakup code.
314 */
315 if (rc == -ETIMEDOUT)
316 {
317 Assert(pTimeout);
318 return VERR_TIMEOUT;
319 }
320 if (rc == -EWOULDBLOCK)
321 /* retry with new value. */;
322 else if (rc == -EINTR)
323 {
324 if (!fAutoResume)
325 return VERR_INTERRUPTED;
326 }
327 else
328 {
329 /* this shouldn't happen! */
330 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
331 return RTErrConvertFromErrno(rc);
332 }
333 }
334 else
335 {
336 /* this can't happen. */
337 if (RT_UNLIKELY(pIntEventSem->iMagic != RTSEMEVENT_MAGIC))
338 return VERR_SEM_DESTROYED;
339 AssertReleaseMsgFailed(("iNew=%d\n", iNew));
340 }
341 }
342}
343
344
345RTDECL(int) RTSemEventWait(RTSEMEVENT EventSem, unsigned cMillies)
346{
347 int rc = rtSemEventWait(EventSem, cMillies, true);
348 Assert(rc != VERR_INTERRUPTED);
349 return rc;
350}
351
352
353RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT EventSem, unsigned cMillies)
354{
355 return rtSemEventWait(EventSem, cMillies, false);
356}
357
358
359
360
361
362RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI pEventMultiSem)
363{
364 /*
365 * Allocate semaphore handle.
366 */
367 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTMULTIINTERNAL));
368 if (pIntEventMultiSem)
369 {
370 pIntEventMultiSem->iMagic = RTSEMEVENTMULTI_MAGIC;
371 pIntEventMultiSem->iState = 0;
372 *pEventMultiSem = pIntEventMultiSem;
373 return VINF_SUCCESS;
374 }
375 return VERR_NO_MEMORY;
376}
377
378
379RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI EventMultiSem)
380{
381 /*
382 * Validate input.
383 */
384 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
385 AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
386 VERR_INVALID_HANDLE);
387
388 /*
389 * Invalidate the semaphore and wake up anyone waiting on it.
390 */
391 ASMAtomicXchgSize(&pIntEventMultiSem->iMagic, RTSEMEVENTMULTI_MAGIC + 1);
392 if (ASMAtomicXchgS32(&pIntEventMultiSem->iState, -1) == 1)
393 {
394 sys_futex(&pIntEventMultiSem->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
395 usleep(1000);
396 }
397
398 /*
399 * Free the semaphore memory and be gone.
400 */
401 RTMemFree(pIntEventMultiSem);
402 return VINF_SUCCESS;
403}
404
405
406RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI EventMultiSem)
407{
408 /*
409 * Validate input.
410 */
411 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
412 AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
413 VERR_INVALID_HANDLE);
414 /*
415 * Signal it.
416 */
417 int32_t iOld = ASMAtomicXchgS32(&pIntEventMultiSem->iState, -1);
418 if (iOld > 0)
419 {
420 /* wake up sleeping threads. */
421 long cWoken = sys_futex(&pIntEventMultiSem->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
422 AssertMsg(cWoken >= 0, ("%ld\n", cWoken)); NOREF(cWoken);
423 }
424 Assert(iOld == 0 || iOld == -1 || iOld == 1);
425 return VINF_SUCCESS;
426}
427
428
429RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI EventMultiSem)
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#ifdef RT_STRICT
438 int32_t i = pIntEventMultiSem->iState;
439 Assert(i == 0 || i == -1 || i == 1);
440#endif
441
442 /*
443 * Reset it.
444 */
445 ASMAtomicCmpXchgS32(&pIntEventMultiSem->iState, 0, -1);
446 return VINF_SUCCESS;
447}
448
449
450static int rtSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies, bool fAutoResume)
451{
452 /*
453 * Validate input.
454 */
455 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
456 AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
457 VERR_INVALID_HANDLE);
458
459 /*
460 * Quickly check whether it's signaled.
461 */
462 int32_t iCur = pIntEventMultiSem->iState;
463 Assert(iCur == 0 || iCur == -1 || iCur == 1);
464 if (iCur == -1)
465 return VINF_SUCCESS;
466 if (!cMillies)
467 return VERR_TIMEOUT;
468
469 /*
470 * Convert timeout value.
471 */
472 struct timespec ts;
473 struct timespec *pTimeout = NULL;
474 if (cMillies != RT_INDEFINITE_WAIT)
475 {
476 ts.tv_sec = cMillies / 1000;
477 ts.tv_nsec = (cMillies % 1000) * 1000000;
478 pTimeout = &ts;
479 }
480
481 /*
482 * The wait loop.
483 */
484 for (unsigned i = 0;; i++)
485 {
486 /*
487 * Start waiting. We only account for there being or having been
488 * threads waiting on the semaphore to keep things simple.
489 */
490 iCur = pIntEventMultiSem->iState;
491 Assert(iCur == 0 || iCur == -1 || iCur == 1);
492 if ( iCur == 1
493 || ASMAtomicCmpXchgS32(&pIntEventMultiSem->iState, 1, 0))
494 {
495 long rc = sys_futex(&pIntEventMultiSem->iState, FUTEX_WAIT, 1, pTimeout, NULL, 0);
496 if (RT_UNLIKELY(pIntEventMultiSem->iMagic != RTSEMEVENTMULTI_MAGIC))
497 return VERR_SEM_DESTROYED;
498 if (rc == 0)
499 return VINF_SUCCESS;
500
501 /*
502 * Act on the wakup code.
503 */
504 if (rc == -ETIMEDOUT)
505 {
506 Assert(pTimeout);
507 return VERR_TIMEOUT;
508 }
509 if (rc == -EWOULDBLOCK)
510 /* retry, the value changed. */;
511 else if (rc == -EINTR)
512 {
513 if (!fAutoResume)
514 return VERR_INTERRUPTED;
515 }
516 else
517 {
518 /* this shouldn't happen! */
519 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
520 return RTErrConvertFromErrno(rc);
521 }
522 }
523 else if (iCur == -1)
524 return VINF_SUCCESS;
525 }
526}
527
528
529RTDECL(int) RTSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
530{
531 int rc = rtSemEventMultiWait(EventMultiSem, cMillies, true);
532 Assert(rc != VERR_INTERRUPTED);
533 return rc;
534}
535
536
537RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
538{
539 return rtSemEventMultiWait(EventMultiSem, cMillies, false);
540}
541
542
543
544
545
546/**
547 * Validate a Mutex semaphore handle passed to one of the interface.
548 *
549 * @returns true if valid.
550 * @returns false if invalid.
551 * @param pIntMutexSem Pointer to the mutex semaphore to validate.
552 */
553inline bool rtsemMutexValid(struct RTSEMMUTEXINTERNAL *pIntMutexSem)
554{
555 if ((uintptr_t)pIntMutexSem < 0x10000)
556 return false;
557
558#ifdef VBOX_REWRITTEN_MUTEX
559 if (pIntMutexSem->iMagic != RTSEMMUTEX_MAGIC)
560 return false;
561
562#endif /* VBOX_REWRITTEN_MUTEX */
563 if (pIntMutexSem->cNesting == (uint32_t)~0)
564 return false;
565
566 return true;
567}
568
569
570#ifndef VBOX_REWRITTEN_MUTEX
571RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX pMutexSem)
572{
573 int rc;
574
575 /*
576 * Allocate semaphore handle.
577 */
578 struct RTSEMMUTEXINTERNAL *pIntMutexSem = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
579 if (pIntMutexSem)
580 {
581 /*
582 * Create the semaphore.
583 */
584 pthread_mutexattr_t MutexAttr;
585 rc = pthread_mutexattr_init(&MutexAttr);
586 if (!rc)
587 {
588 rc = pthread_mutex_init(&pIntMutexSem->Mutex, &MutexAttr);
589 if (!rc)
590 {
591 pthread_mutexattr_destroy(&MutexAttr);
592
593 pIntMutexSem->Owner = (pthread_t)~0;
594 pIntMutexSem->cNesting = 0;
595
596 *pMutexSem = pIntMutexSem;
597 return VINF_SUCCESS;
598 }
599 pthread_mutexattr_destroy(&MutexAttr);
600 }
601 RTMemFree(pIntMutexSem);
602 }
603 else
604 rc = VERR_NO_MEMORY;
605
606 return rc;
607}
608
609
610RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX MutexSem)
611{
612 /*
613 * Validate input.
614 */
615 if (!rtsemMutexValid(MutexSem))
616 {
617 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
618 return VERR_INVALID_HANDLE;
619 }
620
621 /*
622 * Try destroy it.
623 */
624 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
625 int rc = pthread_mutex_destroy(&pIntMutexSem->Mutex);
626 if (rc)
627 {
628 AssertMsgFailed(("Failed to destroy mutex sem %p, rc=%d.\n", MutexSem, rc));
629 return RTErrConvertFromErrno(rc);
630 }
631
632 /*
633 * Free the memory and be gone.
634 */
635 pIntMutexSem->Owner = (pthread_t)~0;
636 pIntMutexSem->cNesting = ~0;
637 RTMemTmpFree(pIntMutexSem);
638
639 return VINF_SUCCESS;
640}
641
642
643RTDECL(int) RTSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies)
644{
645 /*
646 * Validate input.
647 */
648 if (!rtsemMutexValid(MutexSem))
649 {
650 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
651 return VERR_INVALID_HANDLE;
652 }
653
654 /*
655 * Check if nested request.
656 */
657 pthread_t Self = pthread_self();
658 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
659 if ( pIntMutexSem->Owner == Self
660 && pIntMutexSem->cNesting > 0)
661 {
662 pIntMutexSem->cNesting++;
663 return VINF_SUCCESS;
664 }
665
666 /*
667 * Lock it.
668 */
669 if (cMillies == RT_INDEFINITE_WAIT)
670 {
671 /* take mutex */
672 int rc = pthread_mutex_lock(&pIntMutexSem->Mutex);
673 if (rc)
674 {
675 AssertMsgFailed(("Failed to lock mutex sem %p, rc=%d.\n", MutexSem, rc)); NOREF(rc);
676 return RTErrConvertFromErrno(rc);
677 }
678 }
679 else
680 {
681 /*
682 * Get current time and calc end of wait time.
683 */
684 struct timespec ts = {0,0};
685 clock_gettime(CLOCK_REALTIME, &ts);
686 if (cMillies != 0)
687 {
688 ts.tv_nsec += (cMillies % 1000) * 1000000;
689 ts.tv_sec += cMillies / 1000;
690 if (ts.tv_nsec >= 1000000000)
691 {
692 ts.tv_nsec -= 1000000000;
693 ts.tv_sec++;
694 }
695 }
696
697 /* take mutex */
698 int rc = pthread_mutex_timedlock(&pIntMutexSem->Mutex, &ts);
699 if (rc)
700 {
701 AssertMsg(rc == ETIMEDOUT, ("Failed to lock mutex sem %p, rc=%d.\n", MutexSem, rc)); NOREF(rc);
702 return RTErrConvertFromErrno(rc);
703 }
704 }
705
706 /*
707 * Set the owner and nesting.
708 */
709 pIntMutexSem->Owner = Self;
710 ASMAtomicXchgU32(&pIntMutexSem->cNesting, 1);
711
712 return VINF_SUCCESS;
713}
714
715
716RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX MutexSem, unsigned cMillies)
717{
718 /* EINTR isn't returned by the wait functions we're using. */
719 return RTSemMutexRequest(MutexSem, cMillies);
720}
721
722
723RTDECL(int) RTSemMutexRelease(RTSEMMUTEX MutexSem)
724{
725 /*
726 * Validate input.
727 */
728 if (!rtsemMutexValid(MutexSem))
729 {
730 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
731 return VERR_INVALID_HANDLE;
732 }
733
734 /*
735 * Check if nested.
736 */
737 pthread_t Self = pthread_self();
738 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
739 if ( pIntMutexSem->Owner != Self
740 || pIntMutexSem->cNesting == (uint32_t)~0)
741 {
742 AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
743 pIntMutexSem, Self, pIntMutexSem->Owner, pIntMutexSem->cNesting));
744 return VERR_NOT_OWNER;
745 }
746
747 /*
748 * If nested we'll just pop a nesting.
749 */
750 if (pIntMutexSem->cNesting > 1)
751 {
752 pIntMutexSem->cNesting--;
753 return VINF_SUCCESS;
754 }
755
756 /*
757 * Clear the state. (cNesting == 1)
758 */
759 pIntMutexSem->Owner = (pthread_t)~0;
760 ASMAtomicXchgU32(&pIntMutexSem->cNesting, 0);
761
762 /*
763 * Unlock mutex semaphore.
764 */
765 int rc = pthread_mutex_unlock(&pIntMutexSem->Mutex);
766 if (rc)
767 {
768 AssertMsgFailed(("Failed to unlock mutex sem %p, rc=%d.\n", MutexSem, rc)); NOREF(rc);
769 return RTErrConvertFromErrno(rc);
770 }
771
772 return VINF_SUCCESS;
773}
774#else /* VBOX_REWRITTEN_MUTEX */
775RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX pMutexSem)
776{
777 /*
778 * Allocate semaphore handle.
779 */
780 struct RTSEMMUTEXINTERNAL *pIntMutexSem = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
781 if (pIntMutexSem)
782 {
783 pIntMutexSem->iMagic = RTSEMMUTEX_MAGIC;
784 pIntMutexSem->iState = 0;
785 pIntMutexSem->Owner = (pthread_t)~0;
786 pIntMutexSem->cNesting = 0;
787
788 *pMutexSem = pIntMutexSem;
789 return VINF_SUCCESS;
790 }
791
792 return VERR_NO_MEMORY;
793}
794
795
796RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX MutexSem)
797{
798 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
799 /*
800 * Validate input.
801 */
802 if (!rtsemMutexValid(pIntMutexSem))
803 {
804 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
805 return VERR_INVALID_HANDLE;
806 }
807
808 /*
809 * Invalidate the semaphore and wake up anyone waiting on it.
810 */
811 ASMAtomicXchgSize(&pIntMutexSem->iMagic, RTSEMMUTEX_MAGIC + 1);
812 if (ASMAtomicXchgS32(&pIntMutexSem->iState, 0) > 0)
813 {
814 sys_futex(&pIntMutexSem->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
815 usleep(1000);
816 }
817 pIntMutexSem->Owner = (pthread_t)~0;
818 pIntMutexSem->cNesting = ~0;
819
820 /*
821 * Free the semaphore memory and be gone.
822 */
823 RTMemFree(pIntMutexSem);
824 return VINF_SUCCESS;
825}
826
827
828static int rtsemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies, bool fAutoResume)
829{
830 /*
831 * Validate input.
832 */
833 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
834 if (!rtsemMutexValid(pIntMutexSem))
835 {
836 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
837 return VERR_INVALID_HANDLE;
838 }
839
840 /*
841 * Check if nested request.
842 */
843 pthread_t Self = pthread_self();
844 if ( pIntMutexSem->Owner == Self
845 && pIntMutexSem->cNesting > 0)
846 {
847 pIntMutexSem->cNesting++;
848 return VINF_SUCCESS;
849 }
850
851 /*
852 * Convert timeout value.
853 */
854 struct timespec ts;
855 struct timespec *pTimeout = NULL;
856 if (cMillies != RT_INDEFINITE_WAIT)
857 {
858 ts.tv_sec = cMillies / 1000;
859 ts.tv_nsec = (cMillies % 1000) * 1000000;
860 pTimeout = &ts;
861 }
862
863 /*
864 * Lock the mutex.
865 */
866 int32_t iOld;
867 ASMAtomicCmpXchgExS32(&pIntMutexSem->iState, 1, 0, &iOld);
868 if (RT_UNLIKELY(iOld != 0))
869 {
870 iOld = ASMAtomicXchgS32(&pIntMutexSem->iState, 2);
871 while (iOld != 0)
872 {
873 /*
874 * Go to sleep.
875 */
876 long rc = sys_futex(&pIntMutexSem->iState, FUTEX_WAIT, 2, pTimeout, NULL, 0);
877 if (RT_UNLIKELY(pIntMutexSem->iMagic != RTSEMMUTEX_MAGIC))
878 return VERR_SEM_DESTROYED;
879
880 /*
881 * Act on the wakup code.
882 */
883 if (rc == -ETIMEDOUT)
884 {
885 Assert(pTimeout);
886 return VERR_TIMEOUT;
887 }
888 if (rc == 0)
889 /* we'll leave the loop now unless another thread is faster */;
890 else if (rc == -EWOULDBLOCK)
891 /* retry with new value. */;
892 else if (rc == -EINTR)
893 {
894 if (!fAutoResume)
895 return VERR_INTERRUPTED;
896 }
897 else
898 {
899 /* this shouldn't happen! */
900 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
901 return RTErrConvertFromErrno(rc);
902 }
903
904 iOld = ASMAtomicXchgS32(&pIntMutexSem->iState, 2);
905 }
906 }
907
908 /*
909 * Set the owner and nesting.
910 */
911 pIntMutexSem->Owner = Self;
912 ASMAtomicXchgU32(&pIntMutexSem->cNesting, 1);
913 return VINF_SUCCESS;
914}
915
916
917RTDECL(int) RTSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies)
918{
919 int rc = rtsemMutexRequest(MutexSem, cMillies, true);
920 Assert(rc != VERR_INTERRUPTED);
921 return rc;
922}
923
924
925RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX MutexSem, unsigned cMillies)
926{
927 return rtsemMutexRequest(MutexSem, cMillies, false);
928}
929
930
931RTDECL(int) RTSemMutexRelease(RTSEMMUTEX MutexSem)
932{
933 /*
934 * Validate input.
935 */
936 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
937 if (!rtsemMutexValid(pIntMutexSem))
938 {
939 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
940 return VERR_INVALID_HANDLE;
941 }
942
943 /*
944 * Check if nested.
945 */
946 pthread_t Self = pthread_self();
947 if ( pIntMutexSem->Owner != Self
948 || pIntMutexSem->cNesting == (uint32_t)~0)
949 {
950 AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
951 pIntMutexSem, Self, pIntMutexSem->Owner, pIntMutexSem->cNesting));
952 return VERR_NOT_OWNER;
953 }
954
955 /*
956 * If nested we'll just pop a nesting.
957 */
958 if (pIntMutexSem->cNesting > 1)
959 {
960 pIntMutexSem->cNesting--;
961 return VINF_SUCCESS;
962 }
963
964 /*
965 * Clear the state. (cNesting == 1)
966 */
967 pIntMutexSem->Owner = (pthread_t)~0;
968 ASMAtomicXchgU32(&pIntMutexSem->cNesting, 0);
969
970 /*
971 * Release the mutex.
972 */
973 int32_t iNew = ASMAtomicDecS32(&pIntMutexSem->iState);
974 if (iNew != 0)
975 {
976 /* somebody is waiting, try wake up one of them. */
977 ASMAtomicXchgS32(&pIntMutexSem->iState, 0);
978 (void)sys_futex(&pIntMutexSem->iState, FUTEX_WAKE, 1, NULL, NULL, 0);
979 }
980 return VINF_SUCCESS;
981}
982#endif /* VBOX_REWRITTEN_MUTEX */
983
984
985
986
987/**
988 * Validate a read-write semaphore handle passed to one of the interface.
989 *
990 * @returns true if valid.
991 * @returns false if invalid.
992 * @param pIntRWSem Pointer to the read-write semaphore to validate.
993 */
994inline bool rtsemRWValid(struct RTSEMRWINTERNAL *pIntRWSem)
995{
996 if ((uintptr_t)pIntRWSem < 0x10000)
997 return false;
998
999 if (pIntRWSem->uCheck != (unsigned)~0)
1000 return false;
1001
1002 return true;
1003}
1004
1005
1006RTDECL(int) RTSemRWCreate(PRTSEMRW pRWSem)
1007{
1008 int rc;
1009
1010 /*
1011 * Allocate handle.
1012 */
1013 struct RTSEMRWINTERNAL *pIntRWSem = (struct RTSEMRWINTERNAL *)RTMemAlloc(sizeof(struct RTSEMRWINTERNAL));
1014 if (pIntRWSem)
1015 {
1016 /*
1017 * Create the rwlock.
1018 */
1019 pthread_rwlockattr_t Attr;
1020 rc = pthread_rwlockattr_init(&Attr);
1021 if (!rc)
1022 {
1023 rc = pthread_rwlock_init(&pIntRWSem->RWLock, &Attr);
1024 if (!rc)
1025 {
1026 pIntRWSem->uCheck = ~0;
1027 pIntRWSem->WROwner = (pthread_t)~0;
1028 *pRWSem = pIntRWSem;
1029 return VINF_SUCCESS;
1030 }
1031 }
1032
1033 rc = RTErrConvertFromErrno(rc);
1034 RTMemFree(pIntRWSem);
1035 }
1036 else
1037 rc = VERR_NO_MEMORY;
1038
1039 return rc;
1040}
1041
1042
1043RTDECL(int) RTSemRWDestroy(RTSEMRW RWSem)
1044{
1045 /*
1046 * Validate input.
1047 */
1048 if (!rtsemRWValid(RWSem))
1049 {
1050 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
1051 return VERR_INVALID_HANDLE;
1052 }
1053
1054 /*
1055 * Try destroy it.
1056 */
1057 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1058 int rc = pthread_rwlock_destroy(&pIntRWSem->RWLock);
1059 if (!rc)
1060 {
1061 pIntRWSem->uCheck = 0;
1062 RTMemFree(pIntRWSem);
1063 rc = VINF_SUCCESS;
1064 }
1065 else
1066 {
1067 AssertMsgFailed(("Failed to destroy read-write sem %p, rc=%d.\n", RWSem, rc));
1068 rc = RTErrConvertFromErrno(rc);
1069 }
1070
1071 return rc;
1072}
1073
1074
1075RTDECL(int) RTSemRWRequestRead(RTSEMRW RWSem, unsigned cMillies)
1076{
1077 /*
1078 * Validate input.
1079 */
1080 if (!rtsemRWValid(RWSem))
1081 {
1082 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
1083 return VERR_INVALID_HANDLE;
1084 }
1085
1086 /*
1087 * Try lock it.
1088 */
1089 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1090 if (cMillies == RT_INDEFINITE_WAIT)
1091 {
1092 /* take rwlock */
1093 int rc = pthread_rwlock_rdlock(&pIntRWSem->RWLock);
1094 if (rc)
1095 {
1096 AssertMsgFailed(("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
1097 return RTErrConvertFromErrno(rc);
1098 }
1099 }
1100 else
1101 {
1102 /*
1103 * Get current time and calc end of wait time.
1104 */
1105 struct timespec ts = {0,0};
1106 clock_gettime(CLOCK_REALTIME, &ts);
1107 if (cMillies != 0)
1108 {
1109 ts.tv_nsec += (cMillies % 1000) * 1000000;
1110 ts.tv_sec += cMillies / 1000;
1111 if (ts.tv_nsec >= 1000000000)
1112 {
1113 ts.tv_nsec -= 1000000000;
1114 ts.tv_sec++;
1115 }
1116 }
1117
1118 /* take rwlock */
1119 int rc = pthread_rwlock_timedrdlock(&pIntRWSem->RWLock, &ts);
1120 if (rc)
1121 {
1122 AssertMsg(rc == ETIMEDOUT, ("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
1123 return RTErrConvertFromErrno(rc);
1124 }
1125 }
1126
1127 return VINF_SUCCESS;
1128}
1129
1130
1131RTDECL(int) RTSemRWRequestReadNoResume(RTSEMRW RWSem, unsigned cMillies)
1132{
1133 /* EINTR isn't returned by the wait functions we're using. */
1134 return RTSemRWRequestRead(RWSem, cMillies);
1135}
1136
1137
1138RTDECL(int) RTSemRWReleaseRead(RTSEMRW RWSem)
1139{
1140 /*
1141 * Validate input.
1142 */
1143 if (!rtsemRWValid(RWSem))
1144 {
1145 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
1146 return VERR_INVALID_HANDLE;
1147 }
1148
1149 /*
1150 * Try unlock it.
1151 */
1152 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1153 if (pIntRWSem->WROwner == pthread_self())
1154 {
1155 AssertMsgFailed(("Tried to read unlock when write owner - read-write sem %p.\n", RWSem));
1156 return VERR_NOT_OWNER;
1157 }
1158 int rc = pthread_rwlock_unlock(&pIntRWSem->RWLock);
1159 if (rc)
1160 {
1161 AssertMsgFailed(("Failed read unlock read-write sem %p, rc=%d.\n", RWSem, rc));
1162 return RTErrConvertFromErrno(rc);
1163 }
1164
1165 return VINF_SUCCESS;
1166}
1167
1168
1169RTDECL(int) RTSemRWRequestWrite(RTSEMRW RWSem, unsigned cMillies)
1170{
1171 /*
1172 * Validate input.
1173 */
1174 if (!rtsemRWValid(RWSem))
1175 {
1176 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
1177 return VERR_INVALID_HANDLE;
1178 }
1179
1180 /*
1181 * Try lock it.
1182 */
1183 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1184 if (cMillies == RT_INDEFINITE_WAIT)
1185 {
1186 /* take rwlock */
1187 int rc = pthread_rwlock_wrlock(&pIntRWSem->RWLock);
1188 if (rc)
1189 {
1190 AssertMsgFailed(("Failed write lock read-write sem %p, rc=%d.\n", RWSem, rc));
1191 return RTErrConvertFromErrno(rc);
1192 }
1193 }
1194 else
1195 {
1196 /*
1197 * Get current time and calc end of wait time.
1198 */
1199 struct timespec ts = {0,0};
1200 clock_gettime(CLOCK_REALTIME, &ts);
1201 if (cMillies != 0)
1202 {
1203 ts.tv_nsec += (cMillies % 1000) * 1000000;
1204 ts.tv_sec += cMillies / 1000;
1205 if (ts.tv_nsec >= 1000000000)
1206 {
1207 ts.tv_nsec -= 1000000000;
1208 ts.tv_sec++;
1209 }
1210 }
1211
1212 /* take rwlock */
1213 int rc = pthread_rwlock_timedwrlock(&pIntRWSem->RWLock, &ts);
1214 if (rc)
1215 {
1216 AssertMsg(rc == ETIMEDOUT, ("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
1217 return RTErrConvertFromErrno(rc);
1218 }
1219 }
1220
1221 ASMAtomicXchgPtr((void * volatile *)&pIntRWSem->WROwner, (void *)pthread_self());
1222
1223 return VINF_SUCCESS;
1224}
1225
1226
1227RTDECL(int) RTSemRWRequestWriteNoResume(RTSEMRW RWSem, unsigned cMillies)
1228{
1229 /* EINTR isn't returned by the wait functions we're using. */
1230 return RTSemRWRequestWrite(RWSem, cMillies);
1231}
1232
1233
1234RTDECL(int) RTSemRWReleaseWrite(RTSEMRW RWSem)
1235{
1236 /*
1237 * Validate input.
1238 */
1239 if (!rtsemRWValid(RWSem))
1240 {
1241 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
1242 return VERR_INVALID_HANDLE;
1243 }
1244
1245 /*
1246 * Try unlock it.
1247 */
1248 pthread_t Self = pthread_self();
1249 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1250 if (pIntRWSem->WROwner != Self)
1251 {
1252 AssertMsgFailed(("Not Write owner!\n"));
1253 return VERR_NOT_OWNER;
1254 }
1255
1256 /*
1257 * Try unlock it.
1258 */
1259 AssertMsg(sizeof(pthread_t) == sizeof(void *), ("pthread_t is not the size of a pointer but %d bytes\n", sizeof(pthread_t)));
1260 ASMAtomicXchgPtr((void * volatile *)&pIntRWSem->WROwner, (void *)(uintptr_t)~0);
1261 int rc = pthread_rwlock_unlock(&pIntRWSem->RWLock);
1262 if (rc)
1263 {
1264 AssertMsgFailed(("Failed write unlock read-write sem %p, rc=%d.\n", RWSem, rc));
1265 return RTErrConvertFromErrno(rc);
1266 }
1267
1268 return VINF_SUCCESS;
1269}
1270
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