VirtualBox

Ignore:
Timestamp:
Oct 11, 2010 6:59:23 PM (14 years ago)
Author:
vboxsync
Message:

IPRT: Implemented RTSemEventWaitEx* for r0drv darwin.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r0drv/darwin/semevent-r0drv-darwin.cpp

    r29255 r33047  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Event Semaphores, Ring-0 Driver, Darwin.
     3 * IPRT - Single Release Event Semaphores, Ring-0 Driver, Darwin.
    44 */
    55
     
    3939#endif
    4040#include <iprt/err.h>
     41#include <iprt/list.h>
     42#include <iprt/lockvalidator.h>
    4143#include <iprt/mem.h>
    4244#include <iprt/mp.h>
    4345#include <iprt/thread.h>
     46#include <iprt/time.h>
    4447
    4548#include "internal/magics.h"
     
    5053*******************************************************************************/
    5154/**
     55 * Waiter entry.  Lives on the stack.
     56 */
     57typedef struct RTSEMEVENTDARWINENTRY
     58{
     59    /** The list node. */
     60    RTLISTNODE  Node;
     61    /** Flag set when waking up the thread by signal or destroy. */
     62    bool volatile fWokenUp;
     63} RTSEMEVENTDARWINENTRY;
     64/** Pointer to waiter entry. */
     65typedef RTSEMEVENTDARWINENTRY *PRTSEMEVENTDARWINENTRY;
     66
     67
     68/**
    5269 * Darwin event semaphore.
    5370 */
     
    5673    /** Magic value (RTSEMEVENT_MAGIC). */
    5774    uint32_t volatile   u32Magic;
    58     /** The number of waiting threads. */
    59     uint32_t volatile   cWaiters;
     75    /** Reference counter. */
     76    uint32_t volatile   cRefs;
     77    /** Set if there are blocked threads. */
     78    bool volatile       fHaveBlockedThreads;
    6079    /** Set if the event object is signaled. */
    61     uint8_t volatile    fSignaled;
    62     /** The number of threads in the process of waking up. */
    63     uint32_t volatile   cWaking;
     80    bool volatile       fSignaled;
     81    /** List of waiting and woken up threads. */
     82    RTLISTNODE          WaitList;
    6483    /** The spinlock protecting us. */
    6584    lck_spin_t         *pSpinlock;
     
    85104    {
    86105        pThis->u32Magic = RTSEMEVENT_MAGIC;
    87         pThis->cWaiters = 0;
    88         pThis->cWaking = 0;
    89         pThis->fSignaled = 0;
     106        pThis->cRefs                = 1;
     107        pThis->fHaveBlockedThreads  = false;
     108        pThis->fSignaled            = false;
     109        RTListInit(&pThis->WaitList);
    90110        Assert(g_pDarwinLockGroup);
    91111        pThis->pSpinlock = lck_spin_alloc_init(g_pDarwinLockGroup, LCK_ATTR_NULL);
     
    102122}
    103123
     124
     125/**
     126 * Retain a reference to the semaphore.
     127 *
     128 * @param   pThis       The semaphore.
     129 */
     130DECLINLINE(void) rtR0SemEventDarwinRetain(PRTSEMEVENTINTERNAL pThis)
     131{
     132    uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
     133    Assert(cRefs && cRefs < 100000);
     134}
     135
     136
     137/**
     138 * Release a reference, destroy the thing if necessary.
     139 *
     140 * @param   pThis       The semaphore.
     141 */
     142DECLINLINE(void) rtR0SemEventDarwinRelease(PRTSEMEVENTINTERNAL pThis)
     143{
     144    if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
     145    {
     146        Assert(pThis->u32Magic != RTSEMEVENT_MAGIC);
     147        lck_spin_destroy(pThis->pSpinlock, g_pDarwinLockGroup);
     148        RTMemFree(pThis);
     149    }
     150}
    104151
    105152RTDECL(int)  RTSemEventDestroy(RTSEMEVENT hEventSem)
     
    113160
    114161    lck_spin_lock(pThis->pSpinlock);
    115     ASMAtomicIncU32(&pThis->u32Magic); /* make the handle invalid */
    116     if (pThis->cWaiters > 0)
    117     {
    118         /* abort waiting thread, last man cleans up. */
    119         ASMAtomicXchgU32(&pThis->cWaking, pThis->cWaking + pThis->cWaiters);
    120         thread_wakeup_prim((event_t)pThis, FALSE /* all threads */, THREAD_RESTART);
    121         lck_spin_unlock(pThis->pSpinlock);
    122     }
    123     else if (pThis->cWaking)
    124         /* the last waking thread is gonna do the cleanup */
    125         lck_spin_unlock(pThis->pSpinlock);
    126     else
    127     {
    128         lck_spin_unlock(pThis->pSpinlock);
    129         lck_spin_destroy(pThis->pSpinlock, g_pDarwinLockGroup);
    130         RTMemFree(pThis);
    131     }
     162
     163    ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC); /* make the handle invalid */
     164    ASMAtomicWriteBool(&pThis->fSignaled, false);
     165
     166    /* abort waiting threads. */
     167    PRTSEMEVENTDARWINENTRY pWaiter;
     168    RTListForEach(&pThis->WaitList, pWaiter, RTSEMEVENTDARWINENTRY, Node)
     169    {
     170        pWaiter->fWokenUp = true;
     171        thread_wakeup_prim((event_t)pWaiter, FALSE /* all threads */, THREAD_RESTART);
     172    }
     173
     174    lck_spin_unlock(pThis->pSpinlock);
     175    rtR0SemEventDarwinRelease(pThis);
    132176
    133177    return VINF_SUCCESS;
     
    145189    RT_ASSERT_INTS_ON();
    146190
     191    rtR0SemEventDarwinRetain(pThis);
     192
    147193    /** @todo should probably disable interrupts here... update
    148194     *        semspinmutex-r0drv-generic.c when done. */
    149195    lck_spin_lock(pThis->pSpinlock);
    150196
    151     if (pThis->cWaiters > 0)
    152     {
    153         ASMAtomicDecU32(&pThis->cWaiters);
    154         ASMAtomicIncU32(&pThis->cWaking);
    155         thread_wakeup_prim((event_t)pThis, TRUE /* one thread */, THREAD_AWAKENED);
    156         /** @todo this isn't safe. a scheduling interrupt on the other cpu while we're in here
    157          * could cause the thread to be timed out before we manage to wake it up and the event
    158          * ends up in the wrong state. ditto for posix signals.
    159          * Update: check the return code; it will return KERN_NOT_WAITING if no one is around. */
    160     }
    161     else
    162         ASMAtomicXchgU8(&pThis->fSignaled, true);
     197    /*
     198     * Wake up one thread.
     199     */
     200    ASMAtomicWriteBool(&pThis->fSignaled, true);
     201
     202    PRTSEMEVENTDARWINENTRY pWaiter;
     203    RTListForEach(&pThis->WaitList, pWaiter, RTSEMEVENTDARWINENTRY, Node)
     204    {
     205        if (!pWaiter->fWokenUp)
     206        {
     207            pWaiter->fWokenUp = true;
     208            thread_wakeup_prim((event_t)pWaiter, FALSE /* all threads */, THREAD_AWAKENED);
     209            ASMAtomicWriteBool(&pThis->fSignaled, false);
     210            break;
     211        }
     212    }
    163213
    164214    lck_spin_unlock(pThis->pSpinlock);
     215    rtR0SemEventDarwinRelease(pThis);
    165216
    166217    RT_ASSERT_PREEMPT_CPUID();
     
    169220
    170221
    171 static int rtSemEventWait(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies, wait_interrupt_t fInterruptible)
    172 {
    173     PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
    174     AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
    175     AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
    176     if (cMillies)
    177         RT_ASSERT_PREEMPTIBLE();
    178 
     222/**
     223 * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
     224 *
     225 * @returns VBox status code.
     226 * @param   pThis           The event semaphore.
     227 * @param   fFlags          See RTSemEventWaitEx.
     228 * @param   uTimeout        See RTSemEventWaitEx.
     229 * @param   pSrcPos         The source code position of the wait.
     230 */
     231static int rtR0SemEventDarwinWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
     232                                  PCRTLOCKVALSRCPOS pSrcPos)
     233{
     234    /*
     235     * Validate the input.
     236     */
     237    AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
     238    AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
     239    AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
     240
     241    rtR0SemEventDarwinRetain(pThis);
    179242    lck_spin_lock(pThis->pSpinlock);
    180243
     244    /*
     245     * In the signaled state?
     246     */
    181247    int rc;
    182     if (pThis->fSignaled)
    183     {
    184         Assert(!pThis->cWaiters);
    185         ASMAtomicXchgU8(&pThis->fSignaled, false);
     248    if (ASMAtomicCmpXchgBool(&pThis->fSignaled, false, true))
    186249        rc = VINF_SUCCESS;
    187     }
    188     else if (!cMillies)
    189         rc = VERR_TIMEOUT;
    190250    else
    191251    {
    192         ASMAtomicIncU32(&pThis->cWaiters);
    193 
    194         wait_result_t rcWait;
    195         if (cMillies == RT_INDEFINITE_WAIT)
    196             rcWait = lck_spin_sleep(pThis->pSpinlock, LCK_SLEEP_DEFAULT, (event_t)pThis, fInterruptible);
     252        /*
     253         * We have to wait. So, we'll need to convert the timeout and figure
     254         * out if it's indefinite or not.
     255         */
     256        uint64_t uNsAbsTimeout = 1;
     257        if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
     258        {
     259            if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
     260                uTimeout = uTimeout < UINT64_MAX / UINT32_C(1000000) * UINT32_C(1000000)
     261                         ? uTimeout * UINT32_C(1000000)
     262                         : UINT64_MAX;
     263            if (uTimeout == UINT64_MAX)
     264                fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
     265            else
     266            {
     267                uint64_t u64Now;
     268                if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
     269                {
     270                    if (uTimeout != 0)
     271                    {
     272                        u64Now = RTTimeSystemNanoTS();
     273                        uNsAbsTimeout = u64Now + uTimeout;
     274                        if (uNsAbsTimeout < u64Now) /* overflow */
     275                            fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
     276                    }
     277                }
     278                else
     279                {
     280                    uNsAbsTimeout = uTimeout;
     281                    u64Now        = RTTimeSystemNanoTS();
     282                    uTimeout      = u64Now < uTimeout ? uTimeout - u64Now : 0;
     283                }
     284            }
     285        }
     286
     287        if (   !(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
     288            && uTimeout == 0)
     289        {
     290            /*
     291             * Poll call, we already checked the condition above so no need to
     292             * wait for anything.
     293             */
     294            rc = VERR_TIMEOUT;
     295        }
    197296        else
    198297        {
    199             uint64_t u64AbsTime;
    200             nanoseconds_to_absolutetime(cMillies * UINT64_C(1000000), &u64AbsTime);
    201             u64AbsTime += mach_absolute_time();
    202 
    203             rcWait = lck_spin_sleep_deadline(pThis->pSpinlock, LCK_SLEEP_DEFAULT,
    204                                              (event_t)pThis, fInterruptible, u64AbsTime);
    205         }
    206         switch (rcWait)
    207         {
    208             case THREAD_AWAKENED:
    209                 Assert(pThis->cWaking > 0);
    210                 if (    !ASMAtomicDecU32(&pThis->cWaking)
    211                     &&  pThis->u32Magic != RTSEMEVENT_MAGIC)
     298            RTSEMEVENTDARWINENTRY Waiter;
     299            Waiter.fWokenUp = false;
     300            RTListAppend(&pThis->WaitList, &Waiter.Node);
     301
     302            for (;;)
     303            {
     304                /*
     305                 * Do the actual waiting.
     306                 */
     307                ASMAtomicWriteBool(&pThis->fHaveBlockedThreads, true);
     308                wait_interrupt_t fInterruptible = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE ? THREAD_ABORTSAFE : THREAD_UNINT;
     309                wait_result_t    rcWait;
     310                if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
     311                    rcWait = lck_spin_sleep(pThis->pSpinlock, LCK_SLEEP_DEFAULT, (event_t)&Waiter, fInterruptible);
     312                else
    212313                {
    213                     /* the event was destroyed after we woke up, as the last thread do the cleanup. */
    214                     lck_spin_unlock(pThis->pSpinlock);
    215                     Assert(g_pDarwinLockGroup);
    216                     lck_spin_destroy(pThis->pSpinlock, g_pDarwinLockGroup);
    217                     RTMemFree(pThis);
    218                     return VINF_SUCCESS;
     314                    uint64_t u64AbsTime;
     315                    nanoseconds_to_absolutetime(uNsAbsTimeout, &u64AbsTime);
     316                    rcWait = lck_spin_sleep_deadline(pThis->pSpinlock, LCK_SLEEP_DEFAULT,
     317                                                     (event_t)&Waiter, fInterruptible, u64AbsTime);
    219318                }
    220                 rc = VINF_SUCCESS;
     319
     320                /*
     321                 * Deal with the wait result.
     322                 */
     323                if (RT_LIKELY(pThis->u32Magic == RTSEMEVENT_MAGIC))
     324                {
     325                    switch (rcWait)
     326                    {
     327                        case THREAD_AWAKENED:
     328                            if (RT_LIKELY(Waiter.fWokenUp))
     329                                rc = VINF_SUCCESS;
     330                            else if (fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE)
     331                                rc = VERR_INTERRUPTED;
     332                            else
     333                                continue; /* Seen this happen after fork/exec/something. */
     334                            break;
     335
     336                        case THREAD_TIMED_OUT:
     337                            Assert(!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE));
     338                            rc = !Waiter.fWokenUp ? VERR_TIMEOUT : VINF_SUCCESS;
     339                            break;
     340
     341                        case THREAD_INTERRUPTED:
     342                            Assert(fInterruptible != THREAD_UNINT);
     343                            rc = !Waiter.fWokenUp ? VERR_INTERRUPTED : VINF_SUCCESS;
     344                            break;
     345
     346                        case THREAD_RESTART:
     347                            AssertMsg(pThis->u32Magic == ~RTSEMEVENT_MAGIC, ("%#x\n", pThis->u32Magic));
     348                            rc = VERR_SEM_DESTROYED;
     349                            break;
     350
     351                        default:
     352                            AssertMsgFailed(("rcWait=%d\n", rcWait));
     353                            rc = VERR_INTERNAL_ERROR_3;
     354                            break;
     355                    }
     356                }
     357                else
     358                    rc = VERR_SEM_DESTROYED;
    221359                break;
    222 
    223             case THREAD_TIMED_OUT:
    224                 Assert(cMillies != RT_INDEFINITE_WAIT);
    225                 ASMAtomicDecU32(&pThis->cWaiters);
    226                 rc = VERR_TIMEOUT;
    227                 break;
    228 
    229             case THREAD_INTERRUPTED:
    230                 Assert(fInterruptible);
    231                 ASMAtomicDecU32(&pThis->cWaiters);
    232                 rc = VERR_INTERRUPTED;
    233                 break;
    234 
    235             case THREAD_RESTART:
    236                 /* Last one out does the cleanup. */
    237                 if (!ASMAtomicDecU32(&pThis->cWaking))
    238                 {
    239                     lck_spin_unlock(pThis->pSpinlock);
    240                     Assert(g_pDarwinLockGroup);
    241                     lck_spin_destroy(pThis->pSpinlock, g_pDarwinLockGroup);
    242                     RTMemFree(pThis);
    243                     return VERR_SEM_DESTROYED;
    244                 }
    245 
    246                 rc = VERR_SEM_DESTROYED;
    247                 break;
    248 
    249             default:
    250                 AssertMsgFailed(("rcWait=%d\n", rcWait));
    251                 rc = VERR_GENERAL_FAILURE;
    252                 break;
     360            }
     361
     362            RTListNodeRemove(&Waiter.Node);
    253363        }
    254364    }
    255365
    256366    lck_spin_unlock(pThis->pSpinlock);
     367    rtR0SemEventDarwinRelease(pThis);
    257368    return rc;
    258369}
    259370
    260371
    261 RTDECL(int)  RTSemEventWait(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
    262 {
    263     return rtSemEventWait(hEventSem, cMillies, THREAD_UNINT);
    264 }
    265 
    266 
    267 RTDECL(int)  RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
    268 {
    269     return rtSemEventWait(hEventSem, cMillies, THREAD_ABORTSAFE);
    270 }
    271 
     372#undef RTSemEventWaitEx
     373RTDECL(int)  RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
     374{
     375#ifndef RTSEMEVENT_STRICT
     376    return rtR0SemEventDarwinWait(hEventSem, fFlags, uTimeout, NULL);
     377#else
     378    RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
     379    return rtR0SemEventDarwinWait(hEventSem, fFlags, uTimeout, &SrcPos);
     380#endif
     381}
     382
     383
     384RTDECL(int)  RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
     385                                   RTHCUINTPTR uId, RT_SRC_POS_DECL)
     386{
     387    RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
     388    return rtR0SemEventDarwinWait(hEventSem, fFlags, uTimeout, &SrcPos);
     389}
     390
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