VirtualBox

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

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

Reimplemented mutex semaphores in sems-linux.cpp (64bit linux has bugs in pthreads lib function pthread_mutex_timedlock causing SEGV). Disabled at the moment as there are performance issues with lwip. The synchronization itself is apparently correct.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 32.8 KB
Line 
1/* $Id: sems-linux.cpp 6727 2008-02-01 17:06:53Z 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 (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 (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 iOld = ASMAtomicXchgS32(&pIntMutexSem->iState, 2);
887 return VERR_TIMEOUT;
888 }
889 if (rc == 0)
890 /* we'll leave the loop now unless another thread is faster */;
891 else if (rc == -EWOULDBLOCK)
892 /* retry with new value. */;
893 else if (rc == -EINTR)
894 {
895 if (!fAutoResume)
896 return VERR_INTERRUPTED;
897 }
898 else
899 {
900 /* this shouldn't happen! */
901 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
902 return RTErrConvertFromErrno(rc);
903 }
904
905 iOld = ASMAtomicXchgS32(&pIntMutexSem->iState, 2);
906 }
907 }
908
909 /*
910 * Set the owner and nesting.
911 */
912 pIntMutexSem->Owner = Self;
913 ASMAtomicXchgU32(&pIntMutexSem->cNesting, 1);
914 return VINF_SUCCESS;
915}
916
917
918RTDECL(int) RTSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies)
919{
920 int rc = rtsemMutexRequest(MutexSem, cMillies, true);
921 Assert(rc != VERR_INTERRUPTED);
922 return rc;
923}
924
925
926RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX MutexSem, unsigned cMillies)
927{
928 return rtsemMutexRequest(MutexSem, cMillies, false);
929}
930
931
932RTDECL(int) RTSemMutexRelease(RTSEMMUTEX MutexSem)
933{
934 /*
935 * Validate input.
936 */
937 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
938 if (!rtsemMutexValid(pIntMutexSem))
939 {
940 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
941 return VERR_INVALID_HANDLE;
942 }
943
944 /*
945 * Check if nested.
946 */
947 pthread_t Self = pthread_self();
948 if ( pIntMutexSem->Owner != Self
949 || pIntMutexSem->cNesting == (uint32_t)~0)
950 {
951 AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
952 pIntMutexSem, Self, pIntMutexSem->Owner, pIntMutexSem->cNesting));
953 return VERR_NOT_OWNER;
954 }
955
956 /*
957 * If nested we'll just pop a nesting.
958 */
959 if (pIntMutexSem->cNesting > 1)
960 {
961 pIntMutexSem->cNesting--;
962 return VINF_SUCCESS;
963 }
964
965 /*
966 * Clear the state. (cNesting == 1)
967 */
968 pIntMutexSem->Owner = (pthread_t)~0;
969 ASMAtomicXchgU32(&pIntMutexSem->cNesting, 0);
970
971 /*
972 * Release the mutex.
973 */
974 int32_t iNew = ASMAtomicDecS32(&pIntMutexSem->iState);
975 if (iNew != 0)
976 {
977 /* somebody is waiting, try wake up one of them. */
978 pIntMutexSem->iState = 0;
979 (void)sys_futex(&pIntMutexSem->iState, FUTEX_WAKE, 1, NULL, NULL, 0);
980 }
981 return VINF_SUCCESS;
982}
983#endif /* VBOX_REWRITTEN_MUTEX */
984
985
986
987
988/**
989 * Validate a read-write semaphore handle passed to one of the interface.
990 *
991 * @returns true if valid.
992 * @returns false if invalid.
993 * @param pIntRWSem Pointer to the read-write semaphore to validate.
994 */
995inline bool rtsemRWValid(struct RTSEMRWINTERNAL *pIntRWSem)
996{
997 if ((uintptr_t)pIntRWSem < 0x10000)
998 return false;
999
1000 if (pIntRWSem->uCheck != (unsigned)~0)
1001 return false;
1002
1003 return true;
1004}
1005
1006
1007RTDECL(int) RTSemRWCreate(PRTSEMRW pRWSem)
1008{
1009 int rc;
1010
1011 /*
1012 * Allocate handle.
1013 */
1014 struct RTSEMRWINTERNAL *pIntRWSem = (struct RTSEMRWINTERNAL *)RTMemAlloc(sizeof(struct RTSEMRWINTERNAL));
1015 if (pIntRWSem)
1016 {
1017 /*
1018 * Create the rwlock.
1019 */
1020 pthread_rwlockattr_t Attr;
1021 rc = pthread_rwlockattr_init(&Attr);
1022 if (!rc)
1023 {
1024 rc = pthread_rwlock_init(&pIntRWSem->RWLock, &Attr);
1025 if (!rc)
1026 {
1027 pIntRWSem->uCheck = ~0;
1028 pIntRWSem->WROwner = (pthread_t)~0;
1029 *pRWSem = pIntRWSem;
1030 return VINF_SUCCESS;
1031 }
1032 }
1033
1034 rc = RTErrConvertFromErrno(rc);
1035 RTMemFree(pIntRWSem);
1036 }
1037 else
1038 rc = VERR_NO_MEMORY;
1039
1040 return rc;
1041}
1042
1043
1044RTDECL(int) RTSemRWDestroy(RTSEMRW RWSem)
1045{
1046 /*
1047 * Validate input.
1048 */
1049 if (!rtsemRWValid(RWSem))
1050 {
1051 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
1052 return VERR_INVALID_HANDLE;
1053 }
1054
1055 /*
1056 * Try destroy it.
1057 */
1058 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1059 int rc = pthread_rwlock_destroy(&pIntRWSem->RWLock);
1060 if (!rc)
1061 {
1062 pIntRWSem->uCheck = 0;
1063 RTMemFree(pIntRWSem);
1064 rc = VINF_SUCCESS;
1065 }
1066 else
1067 {
1068 AssertMsgFailed(("Failed to destroy read-write sem %p, rc=%d.\n", RWSem, rc));
1069 rc = RTErrConvertFromErrno(rc);
1070 }
1071
1072 return rc;
1073}
1074
1075
1076RTDECL(int) RTSemRWRequestRead(RTSEMRW RWSem, unsigned cMillies)
1077{
1078 /*
1079 * Validate input.
1080 */
1081 if (!rtsemRWValid(RWSem))
1082 {
1083 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
1084 return VERR_INVALID_HANDLE;
1085 }
1086
1087 /*
1088 * Try lock it.
1089 */
1090 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1091 if (cMillies == RT_INDEFINITE_WAIT)
1092 {
1093 /* take rwlock */
1094 int rc = pthread_rwlock_rdlock(&pIntRWSem->RWLock);
1095 if (rc)
1096 {
1097 AssertMsgFailed(("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
1098 return RTErrConvertFromErrno(rc);
1099 }
1100 }
1101 else
1102 {
1103 /*
1104 * Get current time and calc end of wait time.
1105 */
1106 struct timespec ts = {0,0};
1107 clock_gettime(CLOCK_REALTIME, &ts);
1108 if (cMillies != 0)
1109 {
1110 ts.tv_nsec += (cMillies % 1000) * 1000000;
1111 ts.tv_sec += cMillies / 1000;
1112 if (ts.tv_nsec >= 1000000000)
1113 {
1114 ts.tv_nsec -= 1000000000;
1115 ts.tv_sec++;
1116 }
1117 }
1118
1119 /* take rwlock */
1120 int rc = pthread_rwlock_timedrdlock(&pIntRWSem->RWLock, &ts);
1121 if (rc)
1122 {
1123 AssertMsg(rc == ETIMEDOUT, ("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
1124 return RTErrConvertFromErrno(rc);
1125 }
1126 }
1127
1128 return VINF_SUCCESS;
1129}
1130
1131
1132RTDECL(int) RTSemRWRequestReadNoResume(RTSEMRW RWSem, unsigned cMillies)
1133{
1134 /* EINTR isn't returned by the wait functions we're using. */
1135 return RTSemRWRequestRead(RWSem, cMillies);
1136}
1137
1138
1139RTDECL(int) RTSemRWReleaseRead(RTSEMRW RWSem)
1140{
1141 /*
1142 * Validate input.
1143 */
1144 if (!rtsemRWValid(RWSem))
1145 {
1146 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
1147 return VERR_INVALID_HANDLE;
1148 }
1149
1150 /*
1151 * Try unlock it.
1152 */
1153 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1154 if (pIntRWSem->WROwner == pthread_self())
1155 {
1156 AssertMsgFailed(("Tried to read unlock when write owner - read-write sem %p.\n", RWSem));
1157 return VERR_NOT_OWNER;
1158 }
1159 int rc = pthread_rwlock_unlock(&pIntRWSem->RWLock);
1160 if (rc)
1161 {
1162 AssertMsgFailed(("Failed read unlock read-write sem %p, rc=%d.\n", RWSem, rc));
1163 return RTErrConvertFromErrno(rc);
1164 }
1165
1166 return VINF_SUCCESS;
1167}
1168
1169
1170RTDECL(int) RTSemRWRequestWrite(RTSEMRW RWSem, unsigned cMillies)
1171{
1172 /*
1173 * Validate input.
1174 */
1175 if (!rtsemRWValid(RWSem))
1176 {
1177 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
1178 return VERR_INVALID_HANDLE;
1179 }
1180
1181 /*
1182 * Try lock it.
1183 */
1184 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1185 if (cMillies == RT_INDEFINITE_WAIT)
1186 {
1187 /* take rwlock */
1188 int rc = pthread_rwlock_wrlock(&pIntRWSem->RWLock);
1189 if (rc)
1190 {
1191 AssertMsgFailed(("Failed write lock read-write sem %p, rc=%d.\n", RWSem, rc));
1192 return RTErrConvertFromErrno(rc);
1193 }
1194 }
1195 else
1196 {
1197 /*
1198 * Get current time and calc end of wait time.
1199 */
1200 struct timespec ts = {0,0};
1201 clock_gettime(CLOCK_REALTIME, &ts);
1202 if (cMillies != 0)
1203 {
1204 ts.tv_nsec += (cMillies % 1000) * 1000000;
1205 ts.tv_sec += cMillies / 1000;
1206 if (ts.tv_nsec >= 1000000000)
1207 {
1208 ts.tv_nsec -= 1000000000;
1209 ts.tv_sec++;
1210 }
1211 }
1212
1213 /* take rwlock */
1214 int rc = pthread_rwlock_timedwrlock(&pIntRWSem->RWLock, &ts);
1215 if (rc)
1216 {
1217 AssertMsg(rc == ETIMEDOUT, ("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
1218 return RTErrConvertFromErrno(rc);
1219 }
1220 }
1221
1222 ASMAtomicXchgPtr((void * volatile *)&pIntRWSem->WROwner, (void *)pthread_self());
1223
1224 return VINF_SUCCESS;
1225}
1226
1227
1228RTDECL(int) RTSemRWRequestWriteNoResume(RTSEMRW RWSem, unsigned cMillies)
1229{
1230 /* EINTR isn't returned by the wait functions we're using. */
1231 return RTSemRWRequestWrite(RWSem, cMillies);
1232}
1233
1234
1235RTDECL(int) RTSemRWReleaseWrite(RTSEMRW RWSem)
1236{
1237 /*
1238 * Validate input.
1239 */
1240 if (!rtsemRWValid(RWSem))
1241 {
1242 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
1243 return VERR_INVALID_HANDLE;
1244 }
1245
1246 /*
1247 * Try unlock it.
1248 */
1249 pthread_t Self = pthread_self();
1250 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1251 if (pIntRWSem->WROwner != Self)
1252 {
1253 AssertMsgFailed(("Not Write owner!\n"));
1254 return VERR_NOT_OWNER;
1255 }
1256
1257 /*
1258 * Try unlock it.
1259 */
1260 AssertMsg(sizeof(pthread_t) == sizeof(void *), ("pthread_t is not the size of a pointer but %d bytes\n", sizeof(pthread_t)));
1261 ASMAtomicXchgPtr((void * volatile *)&pIntRWSem->WROwner, (void *)(uintptr_t)~0);
1262 int rc = pthread_rwlock_unlock(&pIntRWSem->RWLock);
1263 if (rc)
1264 {
1265 AssertMsgFailed(("Failed write unlock read-write sem %p, rc=%d.\n", RWSem, rc));
1266 return RTErrConvertFromErrno(rc);
1267 }
1268
1269 return VINF_SUCCESS;
1270}
1271
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