VirtualBox

Changeset 822 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Feb 10, 2007 4:30:51 AM (18 years ago)
Author:
vboxsync
Message:

A quick futex based RTSEMEVENT and RTSEMEVENTMULTI implementation, only for AMD64 were we can safely require 2.6.x. Still some rough edges to fix, but it seems to work now.

Location:
trunk/src/VBox/Runtime
Files:
1 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/Makefile

    r762 r822  
    265265        r3/posix/path-posix.cpp \
    266266        r3/posix/process-posix.cpp \
    267         r3/posix/sems-posix.cpp \
    268267        r3/posix/system-posix.cpp \
    269268        r3/posix/thread-posix.cpp \
     
    272271        generic/utf16locale-generic.cpp \
    273272        r3/posix/utf8-posix.cpp \
    274         timesup.cpp \
     273        timesup.cpp
     274ifeq ($(BUILD_TARGET_ARCH),amd64)
     275 RuntimeR3_SOURCES.linux += r3/linux/sems-linux.cpp
     276else
     277 RuntimeR3_SOURCES.linux += r3/posix/sems-posix.cpp
     278endif
     279
    275280
    276281RuntimeR3_SOURCES.os2   = \
  • trunk/src/VBox/Runtime/r3/linux/sems-linux.cpp

    r820 r822  
    11/* $Id$ */
    22/** @file
    3  * InnoTek Portable Runtime - Semaphores, POSIX.
     3 * InnoTek Portable Runtime - Semaphores, Linux (AMD64 only ATM).
    44 */
    55
     
    3030
    3131#include <errno.h>
     32#include <limits.h>
    3233#include <pthread.h>
    3334#include <unistd.h>
    3435#include <sys/time.h>
    35 
    36 #ifdef __DARWIN__
    37 # define pthread_yield() pthread_yield_np()
    38 #endif
     36#include <sys/syscall.h>
     37#include <linux/futex.h>
    3938
    4039
     
    4342*******************************************************************************/
    4443
    45 /** Internal representation of the POSIX implementation of an Event semaphore.
    46  * The POSIX implementation uses a mutex and a condition variable to implement
    47  * the automatic reset event semaphore semantics.
    48  *
    49  * This must be identical to RTSEMEVENTMULTIINTERNAL!
     44/**
     45 * Linux (single wakup) event semaphore.
    5046 */
    5147struct RTSEMEVENTINTERNAL
    5248{
    53     /** pthread condition. */
    54     pthread_cond_t      Cond;
    55     /** pthread mutex which protects the condition and the event state. */
    56     pthread_mutex_t     Mutex;
    57     /** The state of the semaphore.
    58      * This is operated while owning mutex and using atomic updating. */
    59     volatile uint32_t   u32State;
    60     /** Number of waiters. */
    61     volatile uint32_t   cWaiters;
     49    /** Magic value. */
     50    intptr_t volatile   iMagic;
     51    /** The futex state variable.
     52     * <0 means signaled.
     53     *  0 means not signaled, no waiters.
     54     * >0 means not signaled, and the value gives the number of waiters.
     55     */
     56    int32_t volatile    cWaiters;
    6257};
    6358
    64 /** Posix internal representation of a Mutex Multi semaphore.
    65  * This must be identical to RTSEMEVENTINTERNAL! */
     59#define RTSEMEVENT_MAGIC    ((intptr_t)-16)
     60
     61
     62/**
     63 * Linux multiple wakup event semaphore.
     64 */
    6665struct RTSEMEVENTMULTIINTERNAL
    6766{
    68     /** pthread condition. */
    69     pthread_cond_t      Cond;
    70     /** pthread mutex which protects the condition and the event state. */
    71     pthread_mutex_t     Mutex;
    72     /** The state of the semaphore.
    73      * This is operated while owning mutex and using atomic updating. */
    74     volatile uint32_t   u32State;
    75     /** Number of waiters. */
    76     volatile uint32_t   cWaiters;
     67    /** Magic value. */
     68    intptr_t volatile   iMagic;
     69    /** The futex state variable.
     70     * -1 means signaled.
     71     *  0 means not signaled, no waiters.
     72     * >0 means not signaled, and the value gives the number of waiters.
     73     */
     74    int32_t volatile    iState;
    7775};
    7876
    79 /** The valus of the u32State variable in a RTSEMEVENTINTERNAL and RTSEMEVENTMULTIINTERNAL.
    80  * @{ */
    81 /** The object isn't initialized. */
    82 #define EVENT_STATE_UNINITIALIZED   0
    83 /** The semaphore is is signaled. */
    84 #define EVENT_STATE_SIGNALED        0xff00ff00
    85 /** The semaphore is not signaled. */
    86 #define EVENT_STATE_NOT_SIGNALED    0x00ff00ff
    87 /** @} */
    88 
    89 
    90 /** Posix internal representation of a Mutex semaphore. */
     77#define RTSEMEVENTMULTI_MAGIC ((intptr_t)-128)
     78
     79/**
     80 * Posix internal representation of a Mutex semaphore.
     81 */
    9182struct RTSEMMUTEXINTERNAL
    9283{
     
    9990};
    10091
    101 /** Posix internal representation of a read-write semaphore. */
     92
     93/**
     94 * Posix internal representation of a read-write semaphore.
     95 */
    10296struct RTSEMRWINTERNAL
    10397{
     
    113107
    114108/**
    115  * Validate an Event semaphore handle passed to one of the interface.
    116  *
    117  * @returns true if valid.
    118  * @returns false if invalid.
    119  * @param   pIntEventSem    Pointer to the event semaphore to validate.
     109 * Wrapper for the futex syscall.
    120110 */
    121 inline bool rtsemEventValid(struct RTSEMEVENTINTERNAL *pIntEventSem)
    122 {
    123     if ((uintptr_t)pIntEventSem < 0x10000)
    124         return false;
    125 
    126     uint32_t    u32 = pIntEventSem->u32State; /* this is volatile, so a explicit read like this is needed. */
    127     if (    u32 != EVENT_STATE_NOT_SIGNALED
    128         &&  u32 != EVENT_STATE_SIGNALED)
    129         return false;
    130 
    131     return true;
    132 }
     111static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
     112{
     113    errno = 0;
     114    long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
     115    if (rc < 0)
     116    {
     117        Assert(rc == -1);
     118        rc = -errno;
     119    }
     120    return rc;
     121}
     122
    133123
    134124
    135125RTDECL(int)  RTSemEventCreate(PRTSEMEVENT pEventSem)
    136126{
    137     int rc;
    138 
    139127    /*
    140128     * Allocate semaphore handle.
     
    143131    if (pIntEventSem)
    144132    {
    145         /*
    146          * Create the condition variable.
     133        pIntEventSem->iMagic = RTSEMEVENT_MAGIC;
     134        pIntEventSem->cWaiters = 0;
     135        *pEventSem = pIntEventSem;
     136        return VINF_SUCCESS;
     137    }
     138    return  VERR_NO_MEMORY;
     139}
     140
     141
     142RTDECL(int)  RTSemEventDestroy(RTSEMEVENT EventSem)
     143{
     144    /*
     145     * Validate input.
     146     */
     147    struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
     148    AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
     149                 VERR_INVALID_HANDLE);
     150
     151    /*
     152     * Invalidate the semaphore and wake up anyone waiting on it.
     153     */
     154    ASMAtomicXchgSize(&pIntEventSem->iMagic, RTSEMEVENT_MAGIC + 1);
     155    if (ASMAtomicXchgS32(&pIntEventSem->cWaiters, INT32_MIN / 2) > 0)
     156    {
     157        sys_futex(&pIntEventSem->cWaiters, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
     158        usleep(1000);
     159    }
     160
     161    /*
     162     * Free the semaphore memory and be gone.
     163     */
     164    RTMemFree(pIntEventSem);
     165    return VINF_SUCCESS;
     166}
     167
     168
     169RTDECL(int)  RTSemEventSignal(RTSEMEVENT EventSem)
     170{
     171    /*
     172     * Validate input.
     173     */
     174    struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
     175    AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
     176                 VERR_INVALID_HANDLE);
     177    /*
     178     * Try signal it.
     179     */
     180    for (unsigned i = 0;; i++)
     181    {
     182        int32_t iCur = pIntEventSem->cWaiters;
     183        if (iCur < 0)
     184            break; /* already signaled */
     185        if (iCur == 0)
     186        {
     187            if (ASMAtomicCmpXchgS32(&pIntEventSem->cWaiters, -1, 0))
     188                break; /* nobody is waiting */
     189        }
     190        else if (ASMAtomicCmpXchgS32(&pIntEventSem->cWaiters, iCur - 1, iCur))
     191        {
     192            /* somebody is waiting, wake up one. */
     193            long cWoken = sys_futex(&pIntEventSem->cWaiters, FUTEX_WAKE, 1, NULL, NULL, 0);
     194            if (cWoken == 1)
     195                break;
     196            ASMAtomicIncS32(&pIntEventSem->cWaiters);
     197            AssertMsg(cWoken == 0, ("%ld\n", cWoken));
     198
     199            /*
     200             * We're waiting for the threads to start executing, that's kind of
     201             * silly really. But for now, take care that we don't prevent them
     202             * from executing.
     203             */
     204            if ((i % 128) == 127)
     205                usleep(1000);
     206            else if ((i % 16) == 15)
     207                pthread_yield();
     208            else
     209                Assert(i < 1024);
     210        }
     211    }
     212    return VINF_SUCCESS;
     213}
     214
     215
     216static int rtSemEventWait(RTSEMEVENT EventSem, unsigned cMillies, bool fAutoResume)
     217{
     218    /*
     219     * Validate input.
     220     */
     221    struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
     222    AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
     223                 VERR_INVALID_HANDLE);
     224
     225    /*
     226     * Quickly check whether it's signaled.
     227     */
     228    if (ASMAtomicCmpXchgS32(&pIntEventSem->cWaiters, 0, -1))
     229        return VINF_SUCCESS;
     230
     231    /*
     232     * Convert timeout value.
     233     */
     234    struct timespec ts;
     235    struct timespec *pTimeout = 0;
     236    if (cMillies != RT_INDEFINITE_WAIT)
     237    {
     238        ts.tv_sec  = cMillies / 1000;
     239        ts.tv_nsec = (cMillies % 1000) * 1000000;
     240        pTimeout = &ts;
     241    }
     242
     243    /*
     244     * The wait loop.
     245     */
     246    for (unsigned i = 0;; i++)
     247    {
     248        /*
     249         * Announce that we're among the waiters.
    147250         */
    148         pthread_condattr_t CondAttr;
    149         rc = pthread_condattr_init(&CondAttr);
    150         if (!rc)
    151         {
    152             rc = pthread_cond_init(&pIntEventSem->Cond, &CondAttr);
    153             if (!rc)
     251        int32_t iNew = ASMAtomicIncS32(&pIntEventSem->cWaiters);
     252        if (iNew == 0)
     253            return VINF_SUCCESS;
     254        if (RT_LIKELY(iNew > 0))
     255        {
     256            /*
     257             * Go to sleep.
     258             */
     259            long rc = sys_futex(&pIntEventSem->cWaiters, FUTEX_WAIT, iNew, pTimeout, NULL, 0);
     260            if (RT_UNLIKELY(pIntEventSem->iMagic != RTSEMEVENT_MAGIC))
     261                return VERR_SEM_DESTROYED;
     262            if (rc == 0)
     263                return VINF_SUCCESS;
     264
     265            /* don't count us among the waiters. */
     266            iNew = ASMAtomicDecS32(&pIntEventSem->cWaiters);
     267            Assert(iNew >= 0);
     268
     269            /*
     270             * Act on the wakup code.
     271             */
     272            if (rc == -ETIMEDOUT)
    154273            {
    155                 /*
    156                  * Create the semaphore.
    157                  */
    158                 pthread_mutexattr_t MutexAttr;
    159                 rc = pthread_mutexattr_init(&MutexAttr);
    160                 if (!rc)
    161                 {
    162                     rc = pthread_mutex_init(&pIntEventSem->Mutex, &MutexAttr);
    163                     if (!rc)
    164                     {
    165                         pthread_mutexattr_destroy(&MutexAttr);
    166                         pthread_condattr_destroy(&CondAttr);
    167 
    168                         ASMAtomicXchgU32(&pIntEventSem->u32State, EVENT_STATE_NOT_SIGNALED);
    169                         ASMAtomicXchgU32(&pIntEventSem->cWaiters, 0);
    170 
    171                         *pEventSem = pIntEventSem;
    172                         return VINF_SUCCESS;
    173                     }
    174                     pthread_mutexattr_destroy(&MutexAttr);
    175                 }
    176                 pthread_cond_destroy(&pIntEventSem->Cond);
     274                Assert(pTimeout);
     275                return VERR_TIMEOUT;
     276            }
     277            if (rc == -EWOULDBLOCK)
     278                /* retry with new value. */;
     279            else if (rc == -EINTR)
     280            {
     281                if (!fAutoResume)
     282                    return VERR_INTERRUPTED;
    177283            }
    178             pthread_condattr_destroy(&CondAttr);
    179         }
    180 
    181         rc = RTErrConvertFromErrno(rc);
    182         RTMemFree(pIntEventSem);
    183     }
    184     else
    185         rc = VERR_NO_MEMORY;
    186 
    187     return rc;
    188 }
    189 
    190 
    191 RTDECL(int)  RTSemEventDestroy(RTSEMEVENT EventSem)
    192 {
    193     /*
    194      * Validate handle.
    195      */
    196     if (!rtsemEventValid(EventSem))
    197     {
    198         AssertMsgFailed(("Invalid handle %p!\n", EventSem));
    199         return VERR_INVALID_HANDLE;
    200     }
    201 
    202     /*
    203      * Abort all waiters forcing them to return failure.
    204      *
    205      */
    206     struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
    207     int rc;
    208     for (int i = 30; i > 0; i--)
    209     {
    210         ASMAtomicXchgU32(&pIntEventSem->u32State, EVENT_STATE_UNINITIALIZED);
    211         rc = pthread_cond_destroy(&pIntEventSem->Cond);
    212         if (rc != EBUSY)
    213             break;
    214         pthread_cond_broadcast(&pIntEventSem->Cond);
    215         usleep(1000);
    216     } while (rc == EBUSY);
    217     if (rc)
    218     {
    219         AssertMsgFailed(("Failed to destroy event sem %p, rc=%d.\n", EventSem, rc));
    220         return RTErrConvertFromErrno(rc);
    221     }
    222 
    223     /*
    224      * Destroy the semaphore
    225      * If it's busy we'll wait a bit to give the threads a chance to be scheduled.
    226      */
    227     for (int i = 30; i > 0; i--)
    228     {
    229         rc = pthread_mutex_destroy(&pIntEventSem->Mutex);
    230         if (rc != EBUSY)
    231             break;
    232         usleep(1000);
    233     }
    234     if (rc)
    235     {
    236         AssertMsgFailed(("Failed to destroy event sem %p, rc=%d. (mutex)\n", EventSem, rc));
    237         return RTErrConvertFromErrno(rc);
    238     }
    239 
    240     /*
    241      * Free the semaphore memory and be gone.
    242      */
    243     RTMemFree(pIntEventSem);
    244     return VINF_SUCCESS;
    245 }
    246 
    247 
    248 RTDECL(int)  RTSemEventSignal(RTSEMEVENT EventSem)
    249 {
    250     /*
    251      * Validate input.
    252      */
    253     if (!rtsemEventValid(EventSem))
    254     {
    255         AssertMsgFailed(("Invalid handle %p!\n", EventSem));
    256         return VERR_INVALID_HANDLE;
    257     }
    258 
    259     /*
    260      * Lock the mutex semaphore.
    261      */
    262     struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
    263     int rc = pthread_mutex_lock(&pIntEventSem->Mutex);
    264     if (rc)
    265     {
    266         AssertMsgFailed(("Failed to lock event sem %p, rc=%d.\n", EventSem, rc));
    267         return RTErrConvertFromErrno(rc);
    268     }
    269 
    270     /*
    271      * Check the state.
    272      */
    273     if (pIntEventSem->u32State == EVENT_STATE_NOT_SIGNALED)
    274     {
    275         ASMAtomicXchgU32(&pIntEventSem->u32State, EVENT_STATE_SIGNALED);
    276         rc = pthread_cond_signal(&pIntEventSem->Cond);
    277         AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d.\n", EventSem, rc));
    278     }
    279     else if (pIntEventSem->u32State == EVENT_STATE_SIGNALED)
    280     {
    281         rc = pthread_cond_signal(&pIntEventSem->Cond); /* give'm another kick... */
    282         AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d. (2)\n", EventSem, rc));
    283     }
    284     else
    285         rc = VERR_SEM_DESTROYED;
    286 
    287     /*
    288      * Release the mutex and return.
    289      */
    290     int rc2 = pthread_mutex_unlock(&pIntEventSem->Mutex);
    291     AssertMsg(!rc2, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc));
    292     if (rc)
    293         return RTErrConvertFromErrno(rc);
    294     if (rc2)
    295         return RTErrConvertFromErrno(rc2);
    296 
    297     return VINF_SUCCESS;
    298 }
    299 
    300 
    301 static int  rtSemEventWait(RTSEMEVENT EventSem, unsigned cMillies, bool fAutoResume)
    302 {
    303     /*
    304      * Validate input.
    305      */
    306     if (!rtsemEventValid(EventSem))
    307     {
    308         AssertMsgFailed(("Invalid handle %p!\n", EventSem));
    309         return VERR_INVALID_HANDLE;
    310     }
    311 
    312     /*
    313      * Timed or indefinite wait?
    314      */
    315     struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
    316     if (cMillies == RT_INDEFINITE_WAIT)
    317     {
    318         /* for fairness, yield before going to sleep. */
    319         if (    ASMAtomicIncU32(&pIntEventSem->cWaiters) > 1
    320             &&  pIntEventSem->u32State == EVENT_STATE_SIGNALED)
    321             pthread_yield();
    322 
    323          /* take mutex */
    324         int rc = pthread_mutex_lock(&pIntEventSem->Mutex);
    325         if (rc)
    326         {
    327             ASMAtomicDecU32(&pIntEventSem->cWaiters);
    328             AssertMsgFailed(("Failed to lock event sem %p, rc=%d.\n", EventSem, rc));
    329             return RTErrConvertFromErrno(rc);
    330         }
    331 
    332         for (;;)
    333         {
    334             /* check state. */
    335             if (pIntEventSem->u32State == EVENT_STATE_SIGNALED)
     284            else
    336285            {
    337                 ASMAtomicXchgU32(&pIntEventSem->u32State, EVENT_STATE_NOT_SIGNALED);
    338                 ASMAtomicDecU32(&pIntEventSem->cWaiters);
    339                 rc = pthread_mutex_unlock(&pIntEventSem->Mutex);
    340                 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
    341                 return VINF_SUCCESS;
    342             }
    343             if (pIntEventSem->u32State == EVENT_STATE_UNINITIALIZED)
    344             {
    345                 rc = pthread_mutex_unlock(&pIntEventSem->Mutex);
    346                 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
    347                 return VERR_SEM_DESTROYED;
    348             }
    349 
    350             /* wait */
    351             rc = pthread_cond_wait(&pIntEventSem->Cond, &pIntEventSem->Mutex);
    352             if (rc)
    353             {
    354                 AssertMsgFailed(("Failed to wait on event sem %p, rc=%d.\n", EventSem, rc));
    355                 ASMAtomicDecU32(&pIntEventSem->cWaiters);
    356                 int rc2 = pthread_mutex_unlock(&pIntEventSem->Mutex);
    357                 AssertMsg(!rc2, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc2)); NOREF(rc2);
     286                /* this shouldn't happen! */
     287                AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
    358288                return RTErrConvertFromErrno(rc);
    359289            }
    360290        }
    361     }
    362     else
    363     {
    364         /*
    365          * Get current time and calc end of wait time.
    366          */
    367         struct timespec     ts = {0,0};
    368 #ifdef __DARWIN__
    369         struct timeval      tv = {0,0};
    370         gettimeofday(&tv, NULL);
    371         ts.tv_sec = tv.tv_sec;
    372         ts.tv_nsec = tv.tv_usec * 1000;
    373 #else
    374         clock_gettime(CLOCK_REALTIME, &ts);
    375 #endif
    376         if (cMillies != 0)
    377         {
    378             ts.tv_nsec += (cMillies % 1000) * 1000000;
    379             ts.tv_sec  += cMillies / 1000;
    380             if (ts.tv_nsec >= 1000000000)
    381             {
    382                 ts.tv_nsec -= 1000000000;
    383                 ts.tv_sec++;
    384             }
    385         }
    386 
    387         /* for fairness, yield before going to sleep. */
    388         if (ASMAtomicIncU32(&pIntEventSem->cWaiters) > 1)
    389             pthread_yield();
    390 
    391         /* take mutex */
    392 #ifdef __DARWIN__
    393         int rc = pthread_mutex_lock(&pIntEventSem->Mutex);
    394 #else
    395         int rc = pthread_mutex_timedlock(&pIntEventSem->Mutex, &ts);
    396 #endif
    397         if (rc)
    398         {
    399             ASMAtomicDecU32(&pIntEventSem->cWaiters);
    400             AssertMsg(rc == ETIMEDOUT, ("Failed to lock event sem %p, rc=%d.\n", EventSem, rc));
    401             return RTErrConvertFromErrno(rc);
    402         }
    403 
    404         for (;;)
    405         {
    406             /* check state. */
    407             if (pIntEventSem->u32State == EVENT_STATE_SIGNALED)
    408             {
    409                 ASMAtomicXchgU32(&pIntEventSem->u32State, EVENT_STATE_NOT_SIGNALED);
    410                 ASMAtomicDecU32(&pIntEventSem->cWaiters);
    411                 rc = pthread_mutex_unlock(&pIntEventSem->Mutex);
    412                 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
    413                 return VINF_SUCCESS;
    414             }
    415             if (pIntEventSem->u32State == EVENT_STATE_UNINITIALIZED)
    416             {
    417                 rc = pthread_mutex_unlock(&pIntEventSem->Mutex);
    418                 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
     291        else
     292        {
     293            /*
     294             * Somebody is signaling the semaphore, let's try again.
     295             * But take care to yield and sleep after a while like always.
     296             * (Could probably change this to string only and return right away...)
     297             */
     298            ASMAtomicDecS32(&pIntEventSem->cWaiters);
     299            if ((i % 128) == 127)
     300                usleep(1000);
     301            else if ((i % 16) == 15)
     302                pthread_yield();
     303            else
     304                AssertReleaseMsg(i < 4096, ("iNew=%d\n", iNew));
     305            if (RT_UNLIKELY(pIntEventSem->iMagic != RTSEMEVENT_MAGIC))
    419306                return VERR_SEM_DESTROYED;
    420             }
    421 
    422             /* wait */
    423             rc = pthread_cond_timedwait(&pIntEventSem->Cond, &pIntEventSem->Mutex, &ts);
    424             if (rc && (rc != EINTR || !fAutoResume)) /* according to SuS this function shall not return EINTR, but linux man page says differently. */
    425             {
    426                 AssertMsg(rc == ETIMEDOUT, ("Failed to wait on event sem %p, rc=%d.\n", EventSem, rc));
    427                 ASMAtomicDecU32(&pIntEventSem->cWaiters);
    428                 int rc2 = pthread_mutex_unlock(&pIntEventSem->Mutex);
    429                 AssertMsg(!rc2, ("Failed to unlock event sem %p, rc2=%d.\n", EventSem, rc2)); NOREF(rc2);
    430                 return RTErrConvertFromErrno(rc);
    431             }
    432         } /* for (;;) */
     307        }
    433308    }
    434309}
     
    452327
    453328
    454 
    455 /**
    456  * Validate an event multi semaphore handle passed to one of the interface.
    457  *
    458  * @returns true if valid.
    459  * @returns false if invalid.
    460  * @param   pIntEventMultiSem    Pointer to the event semaphore to validate.
    461  */
    462 inline bool rtsemEventMultiValid(struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem)
    463 {
    464     if ((uintptr_t)pIntEventMultiSem < 0x10000)
    465         return false;
    466 
    467     uint32_t    u32 = pIntEventMultiSem->u32State; /* this is volatile, so a explicit read like this is needed. */
    468     if (    u32 != EVENT_STATE_NOT_SIGNALED
    469         &&  u32 != EVENT_STATE_SIGNALED)
    470         return false;
    471 
    472     return true;
    473 }
    474 
    475 
    476329RTDECL(int)  RTSemEventMultiCreate(PRTSEMEVENTMULTI pEventMultiSem)
    477330{
    478     /* the code and the structure is identical with other type for this function. */
    479     return RTSemEventCreate((PRTSEMEVENT)pEventMultiSem);
     331    /*
     332     * Allocate semaphore handle.
     333     */
     334    struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTMULTIINTERNAL));
     335    if (pIntEventMultiSem)
     336    {
     337        pIntEventMultiSem->iMagic = RTSEMEVENTMULTI_MAGIC;
     338        pIntEventMultiSem->iState = 0;
     339        *pEventMultiSem = pIntEventMultiSem;
     340        return VINF_SUCCESS;
     341    }
     342    return  VERR_NO_MEMORY;
    480343}
    481344
     
    483346RTDECL(int)  RTSemEventMultiDestroy(RTSEMEVENTMULTI EventMultiSem)
    484347{
    485     /* the code and the structure is identical with other type for this function. */
    486     return RTSemEventDestroy((RTSEMEVENT)EventMultiSem);
     348    /*
     349     * Validate input.
     350     */
     351    struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
     352    AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
     353                 VERR_INVALID_HANDLE);
     354
     355    /*
     356     * Invalidate the semaphore and wake up anyone waiting on it.
     357     */
     358    ASMAtomicXchgSize(&pIntEventMultiSem->iMagic, RTSEMEVENTMULTI_MAGIC + 1);
     359    if (ASMAtomicXchgS32(&pIntEventMultiSem->iState, -1) == 1)
     360    {
     361        sys_futex(&pIntEventMultiSem->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
     362        usleep(1000);
     363    }
     364
     365    /*
     366     * Free the semaphore memory and be gone.
     367     */
     368    RTMemFree(pIntEventMultiSem);
     369    return VINF_SUCCESS;
    487370}
    488371
     
    490373RTDECL(int)  RTSemEventMultiSignal(RTSEMEVENTMULTI EventMultiSem)
    491374{
    492     /* the code and the structure is identical with other type for this function. */
    493     return RTSemEventSignal((RTSEMEVENT)EventMultiSem);
     375    /*
     376     * Validate input.
     377     */
     378    struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
     379    AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
     380                 VERR_INVALID_HANDLE);
     381    /*
     382     * Signal it.
     383     */
     384    int32_t iOld = ASMAtomicXchgS32(&pIntEventMultiSem->iState, -1);
     385    if (iOld > 0)
     386    {
     387        /* wake up sleeping threads. */
     388        long cWoken = sys_futex(&pIntEventMultiSem->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
     389        AssertMsg(cWoken >= 0, ("%ld\n", cWoken)); NOREF(cWoken);
     390    }
     391    Assert(iOld == 0 || iOld == -1 || iOld == 1);
     392    return VINF_SUCCESS;
    494393}
    495394
     
    500399     * Validate input.
    501400     */
    502     if (!rtsemEventMultiValid(EventMultiSem))
    503     {
    504         AssertMsgFailed(("Invalid handle %p!\n", EventMultiSem));
    505         return VERR_INVALID_HANDLE;
    506     }
    507 
    508     /*
    509      * Lock the mutex semaphore.
    510      */
    511401    struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
    512     int rc = pthread_mutex_lock(&pIntEventMultiSem->Mutex);
    513     if (rc)
    514     {
    515         AssertMsgFailed(("Failed to lock event multi sem %p, rc=%d.\n", EventMultiSem, rc));
    516         return RTErrConvertFromErrno(rc);
    517     }
    518 
    519     /*
    520      * Check the state.
    521      */
    522     if (pIntEventMultiSem->u32State == EVENT_STATE_SIGNALED)
    523         ASMAtomicXchgU32(&pIntEventMultiSem->u32State, EVENT_STATE_NOT_SIGNALED);
    524     else if (pIntEventMultiSem->u32State != EVENT_STATE_NOT_SIGNALED)
    525         rc = VERR_SEM_DESTROYED;
    526 
    527     /*
    528      * Release the mutex and return.
    529      */
    530     rc = pthread_mutex_unlock(&pIntEventMultiSem->Mutex);
    531     if (rc)
    532     {
    533         AssertMsgFailed(("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc));
    534         return RTErrConvertFromErrno(rc);
    535     }
    536 
    537     return VINF_SUCCESS;
    538 
     402    AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
     403                 VERR_INVALID_HANDLE);
     404#ifdef RT_STRICT
     405    int32_t i = pIntEventMultiSem->iState;
     406    Assert(i == 0 || i == -1 || i == 1);
     407#endif
     408
     409    /*
     410     * Reset it.
     411     */
     412    ASMAtomicCmpXchgS32(&pIntEventMultiSem->iState, 0, -1);
     413    return VINF_SUCCESS;
    539414}
    540415
     
    545420     * Validate input.
    546421     */
    547     if (!rtsemEventMultiValid(EventMultiSem))
    548     {
    549         AssertMsgFailed(("Invalid handle %p!\n", EventMultiSem));
    550         return VERR_INVALID_HANDLE;
    551     }
    552 
    553     /*
    554      * Timed or indefinite wait?
    555      */
    556422    struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
    557     if (cMillies == RT_INDEFINITE_WAIT)
    558     {
    559         /* take mutex */
    560         int rc = pthread_mutex_lock(&pIntEventMultiSem->Mutex);
    561         if (rc)
    562         {
    563             AssertMsgFailed(("Failed to lock event multi sem %p, rc=%d.\n", EventMultiSem, rc));
    564             return RTErrConvertFromErrno(rc);
    565         }
    566         ASMAtomicIncU32(&pIntEventMultiSem->cWaiters);
    567 
    568         for (;;)
    569         {
    570             /* check state. */
    571             if (pIntEventMultiSem->u32State == EVENT_STATE_SIGNALED)
     423    AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
     424                 VERR_INVALID_HANDLE);
     425
     426    /*
     427     * Quickly check whether it's signaled.
     428     */
     429    int32_t iCur = pIntEventMultiSem->iState;
     430    Assert(iCur == 0 || iCur == -1 || iCur == 1);
     431    if (iCur == -1)
     432        return VINF_SUCCESS;
     433    if (!cMillies)
     434        return VERR_TIMEOUT;
     435
     436    /*
     437     * Convert timeout value.
     438     */
     439    struct timespec ts;
     440    struct timespec *pTimeout = NULL;
     441    if (cMillies != RT_INDEFINITE_WAIT)
     442    {
     443        ts.tv_sec  = cMillies / 1000;
     444        ts.tv_nsec = (cMillies % 1000) * 1000000;
     445        pTimeout = &ts;
     446    }
     447
     448    /*
     449     * The wait loop.
     450     */
     451    for (unsigned i = 0;; i++)
     452    {
     453        /*
     454         * Start waiting. We only account for there being or having been
     455         * threads waiting on the semaphore to keep things simple.
     456         */
     457        iCur = pIntEventMultiSem->iState;
     458        Assert(iCur == 0 || iCur == -1 || iCur == 1);
     459        if (    iCur == 1
     460            ||  ASMAtomicCmpXchgS32(&pIntEventMultiSem->iState, 1, 0))
     461        {
     462            long rc = sys_futex(&pIntEventMultiSem->iState, FUTEX_WAIT, 1, pTimeout, NULL, 0);
     463            if (RT_UNLIKELY(pIntEventMultiSem->iMagic != RTSEMEVENTMULTI_MAGIC))
     464                return VERR_SEM_DESTROYED;
     465            if (rc == 0)
     466                return VINF_SUCCESS;
     467
     468            /*
     469             * Act on the wakup code.
     470             */
     471            if (rc == -ETIMEDOUT)
    572472            {
    573                 ASMAtomicDecU32(&pIntEventMultiSem->cWaiters);
    574                 rc = pthread_mutex_unlock(&pIntEventMultiSem->Mutex);
    575                 AssertMsg(!rc, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc)); NOREF(rc);
    576                 return VINF_SUCCESS;
     473                Assert(pTimeout);
     474                return VERR_TIMEOUT;
     475            }
     476            if (rc == -EWOULDBLOCK)
     477                /* retry, the value changed. */;
     478            else if (rc == -EINTR)
     479            {
     480                if (!fAutoResume)
     481                    return VERR_INTERRUPTED;
    577482            }
    578             if (pIntEventMultiSem->u32State == EVENT_STATE_UNINITIALIZED)
     483            else
    579484            {
    580                 rc = pthread_mutex_unlock(&pIntEventMultiSem->Mutex);
    581                 AssertMsg(!rc, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc)); NOREF(rc);
    582                 return VERR_SEM_DESTROYED;
    583             }
    584 
    585             /* wait */
    586             rc = pthread_cond_wait(&pIntEventMultiSem->Cond, &pIntEventMultiSem->Mutex);
    587             if (rc)
    588             {
    589                 AssertMsgFailed(("Failed to wait on event multi sem %p, rc=%d.\n", EventMultiSem, rc));
    590                 ASMAtomicDecU32(&pIntEventMultiSem->cWaiters);
    591                 int rc2 = pthread_mutex_unlock(&pIntEventMultiSem->Mutex);
    592                 AssertMsg(!rc2, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc2)); NOREF(rc2);
     485                /* this shouldn't happen! */
     486                AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
    593487                return RTErrConvertFromErrno(rc);
    594488            }
    595489        }
    596     }
    597     else
    598     {
    599         /*
    600          * Get current time and calc end of wait time.
    601          */
    602         struct timespec     ts = {0,0};
    603 #ifdef __DARWIN__
    604         struct timeval      tv = {0,0};
    605         gettimeofday(&tv, NULL);
    606         ts.tv_sec = tv.tv_sec;
    607         ts.tv_nsec = tv.tv_usec * 1000;
    608 #else
    609         clock_gettime(CLOCK_REALTIME, &ts);
    610 #endif
    611         if (cMillies != 0)
    612         {
    613             ts.tv_nsec += (cMillies % 1000) * 1000000;
    614             ts.tv_sec  += cMillies / 1000;
    615             if (ts.tv_nsec >= 1000000000)
    616             {
    617                 ts.tv_nsec -= 1000000000;
    618                 ts.tv_sec++;
    619             }
    620         }
    621 
    622         /* take mutex */
    623 #ifdef __DARWIN__
    624         int rc = pthread_mutex_lock(&pIntEventMultiSem->Mutex);
    625 #else
    626         int rc = pthread_mutex_timedlock(&pIntEventMultiSem->Mutex, &ts);
    627 #endif
    628         if (rc)
    629         {
    630             AssertMsg(rc == ETIMEDOUT, ("Failed to lock event multi sem %p, rc=%d.\n", EventMultiSem, rc));
    631             return RTErrConvertFromErrno(rc);
    632         }
    633         ASMAtomicIncU32(&pIntEventMultiSem->cWaiters);
    634 
    635         for (;;)
    636         {
    637             /* check state. */
    638             if (pIntEventMultiSem->u32State == EVENT_STATE_SIGNALED)
    639             {
    640                 ASMAtomicXchgU32(&pIntEventMultiSem->u32State, EVENT_STATE_NOT_SIGNALED);
    641                 ASMAtomicDecU32(&pIntEventMultiSem->cWaiters);
    642                 rc = pthread_mutex_unlock(&pIntEventMultiSem->Mutex);
    643                 AssertMsg(!rc, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc)); NOREF(rc);
    644                 return VINF_SUCCESS;
    645             }
    646             if (pIntEventMultiSem->u32State == EVENT_STATE_UNINITIALIZED)
    647             {
    648                 rc = pthread_mutex_unlock(&pIntEventMultiSem->Mutex);
    649                 AssertMsg(!rc, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc)); NOREF(rc);
    650                 return VERR_SEM_DESTROYED;
    651             }
    652 
    653             /* wait */
    654             rc = pthread_cond_timedwait(&pIntEventMultiSem->Cond, &pIntEventMultiSem->Mutex, &ts);
    655             if (rc && (rc != EINTR || !fAutoResume)) /* according to SuS this function shall not return EINTR, but linux man page says differently. */
    656             {
    657                 AssertMsg(rc == ETIMEDOUT, ("Failed to wait on event multi sem %p, rc=%d.\n", EventMultiSem, rc));
    658                 ASMAtomicDecU32(&pIntEventMultiSem->cWaiters);
    659                 int rc2 = pthread_mutex_unlock(&pIntEventMultiSem->Mutex);
    660                 AssertMsg(!rc2, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc2)); NOREF(rc2);
    661                 return RTErrConvertFromErrno(rc);
    662             }
    663         }
     490        else if (iCur == -1)
     491            return VINF_SUCCESS;
    664492    }
    665493}
     
    812640    else
    813641    {
    814 #ifdef __DARWIN__
    815         AssertMsgFailed(("Not implemented on Darwin yet because of incomplete pthreads API."));
    816         return VERR_NOT_IMPLEMENTED;
    817 #else /* !__DARWIN__ */
    818642        /*
    819643         * Get current time and calc end of wait time.
     
    839663            return RTErrConvertFromErrno(rc);
    840664        }
    841 #endif /* !__DARWIN__ */
    842665    }
    843666
     
    1030853    else
    1031854    {
    1032 #ifdef __DARWIN__
    1033         AssertMsgFailed(("Not implemented on Darwin yet because of incomplete pthreads API."));
    1034         return VERR_NOT_IMPLEMENTED;
    1035 #else /* !__DARWIN__ */
    1036855        /*
    1037856         * Get current time and calc end of wait time.
     
    1057876            return RTErrConvertFromErrno(rc);
    1058877        }
    1059 #endif /* !__DARWIN__ */
    1060878    }
    1061879
     
    1129947    else
    1130948    {
    1131 #ifdef __DARWIN__
    1132         AssertMsgFailed(("Not implemented on Darwin yet because of incomplete pthreads API."));
    1133         return VERR_NOT_IMPLEMENTED;
    1134 #else /* !__DARWIN__ */
    1135949        /*
    1136950         * Get current time and calc end of wait time.
     
    1156970            return RTErrConvertFromErrno(rc);
    1157971        }
    1158 #endif /* !__DARWIN__ */
    1159972    }
    1160973
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette