VirtualBox

Changeset 33262 in vbox


Ignore:
Timestamp:
Oct 20, 2010 2:23:32 PM (15 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
66821
Message:

RTOnce: Avoid allocating anything when there are no races.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/once.h

    r28800 r33262  
    5858typedef RTONCE *PRTONCE;
    5959
     60/**
     61 * The execute once statemachine.
     62 */
     63typedef enum RTONCESTATE
     64{
     65    /** RTOnce() has not been called.
     66     *  Next: NO_SEM */
     67    RTONCESTATE_UNINITIALIZED = 1,
     68    /** RTOnce() is busy, no race.
     69     *  Next: CREATING_SEM, DONE */
     70    RTONCESTATE_BUSY_NO_SEM,
     71    /** More than one RTOnce() caller is busy.
     72     *  Next: BUSY_HAVE_SEM, BUSY_SPIN, DONE_CREATING_SEM, DONE */
     73    RTONCESTATE_BUSY_CREATING_SEM,
     74    /** More than one RTOnce() caller, the first is busy, the others are
     75     *  waiting.
     76     *  Next: DONE */
     77    RTONCESTATE_BUSY_HAVE_SEM,
     78    /** More than one RTOnce() caller, the first is busy, the others failed to
     79     *  create a semaphore and are spinning.
     80     *  Next: DONE */
     81    RTONCESTATE_BUSY_SPIN,
     82    /** More than one RTOnce() caller, the first has completed, the others
     83     *  are busy creating the semaphore.
     84     *  Next: DONE_HAVE_SEM */
     85    RTONCESTATE_DONE_CREATING_SEM,
     86    /** More than one RTOnce() caller, the first is busy grabbing the
     87     *  semaphore, while the others are waiting.
     88     *  Next: DONE */
     89    RTONCESTATE_DONE_HAVE_SEM,
     90    /** The execute once stuff has completed. */
     91    RTONCESTATE_DONE = 16
     92} RTONCESTATE;
     93
    6094/** Static initializer for RTONCE variables. */
    61 #define RTONCE_INITIALIZER      { NIL_RTSEMEVENTMULTI, 0, -1, VERR_INTERNAL_ERROR }
     95#define RTONCE_INITIALIZER      { NIL_RTSEMEVENTMULTI, 0, RTONCESTATE_UNINITIALIZED, VERR_INTERNAL_ERROR }
    6296
    6397
  • trunk/src/VBox/Runtime/common/misc/once.cpp

    r28800 r33262  
    3939
    4040
     41/**
     42 * The state loop of the other threads.
     43 *
     44 * @returns VINF_SUCCESS when everything went smoothly. IPRT status code if we
     45 *          encountered trouble.
     46 * @param   pOnce           The execute once structure.
     47 * @param   phEvtM          Where to store the semaphore handle so the caller
     48 *                          can do the cleaning up for us.
     49 */
     50static int rtOnceOtherThread(PRTONCE pOnce, PRTSEMEVENTMULTI phEvtM)
     51{
     52    uint32_t cYields = 0;
     53    for (;;)
     54    {
     55        int32_t iState = ASMAtomicReadS32(&pOnce->iState);
     56        switch (iState)
     57        {
     58            /*
     59             * No semaphore, try create one.
     60             */
     61            case RTONCESTATE_BUSY_NO_SEM:
     62                if (ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_BUSY_CREATING_SEM, RTONCESTATE_BUSY_NO_SEM))
     63                {
     64                    int rc = RTSemEventMultiCreate(phEvtM);
     65                    if (RT_SUCCESS(rc))
     66                    {
     67                        ASMAtomicWriteHandle(&pOnce->hEventMulti, *phEvtM);
     68                        int32_t cRefs = ASMAtomicIncS32(&pOnce->cEventRefs); Assert(cRefs == 1); NOREF(cRefs);
     69
     70                        if (!ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_BUSY_HAVE_SEM, RTONCESTATE_BUSY_CREATING_SEM))
     71                        {
     72                            /* Too slow. */
     73                            AssertReturn(ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE, RTONCESTATE_DONE_CREATING_SEM)
     74                                         , VERR_INTERNAL_ERROR_5);
     75
     76                            ASMAtomicWriteHandle(&pOnce->hEventMulti, NIL_RTSEMEVENTMULTI);
     77                            cRefs = ASMAtomicDecS32(&pOnce->cEventRefs); Assert(cRefs == 0);
     78
     79                            RTSemEventMultiDestroy(*phEvtM);
     80                            *phEvtM = NIL_RTSEMEVENTMULTI;
     81                        }
     82                    }
     83                    else
     84                    {
     85                        AssertReturn(   ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_BUSY_SPIN, RTONCESTATE_BUSY_CREATING_SEM)
     86                                     || ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE,      RTONCESTATE_DONE_CREATING_SEM)
     87                                     , VERR_INTERNAL_ERROR_4);
     88                        *phEvtM = NIL_RTSEMEVENTMULTI;
     89                    }
     90                }
     91                break;
     92
     93            /*
     94             * This isn't nice, but it's the easy way out.
     95             */
     96            case RTONCESTATE_BUSY_CREATING_SEM:
     97            case RTONCESTATE_BUSY_SPIN:
     98                cYields++;
     99                if (!(++cYields % 8))
     100                    RTThreadSleep(1);
     101                else
     102                    RTThreadYield();
     103                break;
     104
     105            /*
     106             * There is a semaphore, try wait on it.
     107             *
     108             * We continue waiting after reaching DONE_HAVE_SEM if we
     109             * already got the semaphore to avoid racing the first thread.
     110             */
     111            case RTONCESTATE_DONE_HAVE_SEM:
     112                if (*phEvtM == NIL_RTSEMEVENTMULTI)
     113                    return VINF_SUCCESS;
     114                /* fall thru */
     115            case RTONCESTATE_BUSY_HAVE_SEM:
     116            {
     117                /*
     118                 * Grab the semaphore if we haven't got it yet.
     119                 * We must take care not to increment the counter if it
     120                 * is 0.  This may happen if we're racing a state change.
     121                 */
     122                if (*phEvtM == NIL_RTSEMEVENTMULTI)
     123                {
     124                    int32_t cEventRefs = ASMAtomicUoReadS32(&pOnce->cEventRefs);
     125                    while (   cEventRefs > 0
     126                           && ASMAtomicUoReadS32(&pOnce->iState) == RTONCESTATE_BUSY_HAVE_SEM)
     127                    {
     128                        if (ASMAtomicCmpXchgExS32(&pOnce->cEventRefs, cEventRefs + 1, cEventRefs, &cEventRefs))
     129                            break;
     130                        ASMNopPause();
     131                    }
     132                    if (cEventRefs <= 0)
     133                        break;
     134
     135                    ASMAtomicReadHandle(&pOnce->hEventMulti, phEvtM);
     136                    AssertReturn(*phEvtM != NIL_RTSEMEVENTMULTI, VERR_INTERNAL_ERROR_2);
     137                }
     138
     139                /*
     140                 * We've got a sempahore, do the actual waiting.
     141                 */
     142                do
     143                    RTSemEventMultiWaitNoResume(*phEvtM, RT_INDEFINITE_WAIT);
     144                while (ASMAtomicReadS32(&pOnce->iState) == RTONCESTATE_BUSY_HAVE_SEM);
     145                break;
     146            }
     147
     148            case RTONCESTATE_DONE_CREATING_SEM:
     149            case RTONCESTATE_DONE:
     150                return VINF_SUCCESS;
     151
     152            default:
     153                AssertMsgFailedReturn(("%d\n", iState), VERR_INTERNAL_ERROR_3);
     154        }
     155    }
     156}
     157
    41158
    42159RTDECL(int) RTOnce(PRTONCE pOnce, PFNRTONCE pfnOnce, void *pvUser1, void *pvUser2)
     
    52169     */
    53170    int32_t iState = ASMAtomicUoReadS32(&pOnce->iState);
    54     if (RT_LIKELY(iState == 2))
     171    if (RT_LIKELY(   iState == RTONCESTATE_DONE
     172                  || iState == RTONCESTATE_DONE_CREATING_SEM
     173                  || iState == RTONCESTATE_DONE_HAVE_SEM
     174                 ))
    55175        return ASMAtomicUoReadS32(&pOnce->rc);
    56     AssertReturn(iState == -1 || iState == 1, VERR_INTERNAL_ERROR);
     176
     177    AssertReturn(   iState == RTONCESTATE_UNINITIALIZED
     178                 || iState == RTONCESTATE_BUSY_NO_SEM
     179                 || iState == RTONCESTATE_BUSY_SPIN
     180                 || iState == RTONCESTATE_BUSY_CREATING_SEM
     181                 || iState == RTONCESTATE_BUSY_HAVE_SEM
     182                 , VERR_INTERNAL_ERROR);
    57183
    58184    /*
    59185     * Do we initialize it?
    60186     */
    61     if (iState == -1)
     187    int32_t rcOnce;
     188    if (   iState == RTONCESTATE_UNINITIALIZED
     189        && ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_BUSY_NO_SEM, RTONCESTATE_UNINITIALIZED))
    62190    {
    63         RTSEMEVENTMULTI hEventMulti;
    64         int rc = RTSemEventMultiCreate(&hEventMulti);
    65         if (RT_FAILURE(rc))
    66             hEventMulti = NIL_RTSEMEVENTMULTI;
    67 
    68         if (ASMAtomicCmpXchgS32(&pOnce->iState, 1, -1))
     191        /*
     192         * Yes, so do the execute once stuff.
     193         */
     194        rcOnce = pfnOnce(pvUser1, pvUser2);
     195        ASMAtomicWriteS32(&pOnce->rc, rcOnce);
     196
     197        /*
     198         * If there is a sempahore to signal, we're in for some extra work here.
     199         */
     200        if (   !ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE,              RTONCESTATE_BUSY_NO_SEM)
     201            && !ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE,              RTONCESTATE_BUSY_SPIN)
     202            && !ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE_CREATING_SEM, RTONCESTATE_BUSY_CREATING_SEM)
     203           )
    69204        {
    70             ASMAtomicWriteHandle(&pOnce->hEventMulti, hEventMulti);
    71             ASMAtomicIncS32(&pOnce->cEventRefs);
    72 
    73             /* do the execute once stuff. */
    74             int32_t rcOnce = pfnOnce(pvUser1, pvUser2);
    75 
    76             /* set the return code, change the state and signal any waiters. */
    77             ASMAtomicWriteS32(&pOnce->rc, rcOnce);
    78             ASMAtomicWriteS32(&pOnce->iState, 2);
    79             if (hEventMulti != NIL_RTSEMEVENTMULTI)
    80                 RTSemEventMultiSignal(hEventMulti);
    81 
    82             /* last guy destroys the semaphore. */
     205            /* Grab the sempahore by switching to 'DONE_HAVE_SEM' before reaching 'DONE'. */
     206            AssertReturn(ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE_HAVE_SEM, RTONCESTATE_BUSY_HAVE_SEM),
     207                         VERR_INTERNAL_ERROR_2);
     208
     209            int32_t cRefs = ASMAtomicIncS32(&pOnce->cEventRefs);
     210            Assert(cRefs > 1);
     211
     212            RTSEMEVENTMULTI hEvtM;
     213            ASMAtomicReadHandle(&pOnce->hEventMulti, &hEvtM);
     214            Assert(hEvtM != NIL_RTSEMEVENTMULTI);
     215
     216            ASMAtomicWriteS32(&pOnce->iState, RTONCESTATE_DONE);
     217
     218            /* Signal it and return. */
     219            RTSemEventMultiSignal(hEvtM);
     220        }
     221    }
     222    else
     223    {
     224        /*
     225         * Wait for the first thread to complete.  Delegate this to a helper
     226         * function to simplify cleanup and keep things a bit shorter.
     227         */
     228        RTSEMEVENTMULTI hEvtM = NIL_RTSEMEVENTMULTI;
     229        rcOnce = rtOnceOtherThread(pOnce, &hEvtM);
     230        if (hEvtM != NIL_RTSEMEVENTMULTI)
     231        {
    83232            if (ASMAtomicDecS32(&pOnce->cEventRefs) == 0)
    84                 ASMAtomicWriteSize(&pOnce->hEventMulti, NIL_RTSEMEVENTMULTI);
    85             else
    86                 hEventMulti = NIL_RTSEMEVENTMULTI;
    87         }
    88         if (hEventMulti != NIL_RTSEMEVENTMULTI)
    89             RTSemEventMultiDestroy(hEventMulti);
    90     }
    91 
    92     /*
    93      * Wait for it to finish initializing.
    94      */
    95     if (ASMAtomicReadS32(&pOnce->iState) == 1)
    96     {
    97         int i = 0;
    98         while (ASMAtomicReadS32(&pOnce->iState) == 1)
    99         {
    100             bool fYieldSleep = true;
    101 
    102             /*
    103              * Take care not to increment the counter if it's 0, that indicates
    104              * that RTONCE::hEventMulti isn't valid either because it's not set
    105              * yet, or because it's being destroyed.
    106              */
    107             int32_t cEventRefs = ASMAtomicUoReadS32(&pOnce->cEventRefs);
    108             while (   cEventRefs > 0
    109                    && !ASMAtomicCmpXchgS32(&pOnce->cEventRefs, cEventRefs + 1, cEventRefs))
    110                 cEventRefs = ASMAtomicUoReadS32(&pOnce->cEventRefs);
    111             if (cEventRefs > 1)
    112233            {
    113                 /*
    114                  * The hEventMulti might be NIL for two reasons, see above in
    115                  * the init code, if it isn't valid just do the yield/sleep thing.
    116                  */
    117                 RTSEMEVENTMULTI hEventMulti;
    118                 ASMAtomicUoReadHandle(&pOnce->hEventMulti, &hEventMulti);
    119                 if (hEventMulti != NIL_RTSEMEVENTMULTI)
    120                 {
    121                     fYieldSleep = false;
    122                     RTSemEventMultiWait(hEventMulti, RT_INDEFINITE_WAIT);
    123                 }
    124 
    125                 /*
    126                  * Last thread cleans up.
    127                  */
    128                 if (ASMAtomicDecS32(&pOnce->cEventRefs) == 0)
    129                 {
    130                     ASMAtomicXchgHandle(&pOnce->hEventMulti, NIL_RTSEMEVENTMULTI, &hEventMulti);
    131                     if (hEventMulti != NIL_RTSEMEVENTMULTI)
    132                         RTSemEventMultiDestroy(hEventMulti);
    133                 }
    134             }
    135 
    136             /*
    137              * If we didn't block, yield or sleep for a bit.
    138              *
    139              * The sleep is essential to prevent higher priority threads from spinning wildly
    140              * and preventing a lower priority thread from completing the pfnOnce operation
    141              * in a timely manner.
    142              */
    143             if (fYieldSleep)
    144             {
    145                 if (ASMAtomicReadS32(&pOnce->iState) != 1)
    146                     break;
    147                 if (!(++i % 8) )
    148                     RTThreadSleep(1);
    149                 else
    150                     RTThreadYield();
     234                bool fRc;
     235                ASMAtomicCmpXchgHandle(&pOnce->hEventMulti, NIL_RTSEMEVENTMULTI, hEvtM, fRc);           Assert(fRc);
     236                fRc = ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_DONE, RTONCESTATE_DONE_HAVE_SEM); Assert(fRc);
     237                RTSemEventMultiDestroy(hEvtM);
    151238            }
    152239        }
     240        if (RT_SUCCESS(rcOnce))
     241            rcOnce = ASMAtomicUoReadS32(&pOnce->rc);
    153242    }
    154243
    155     /*
    156      * Finally, return the status code from the execute once function.
    157      */
    158     return ASMAtomicUoReadS32(&pOnce->rc);
     244    return rcOnce;
    159245}
    160246RT_EXPORT_SYMBOL(RTOnce);
     
    166252    AssertPtr(pOnce);
    167253    Assert(pOnce->hEventMulti == NIL_RTSEMEVENTMULTI);
    168     Assert(pOnce->iState != 1);
     254    int32_t iState = ASMAtomicUoReadS32(&pOnce->iState);
     255    AssertMsg(   iState == RTONCESTATE_DONE
     256              && iState == RTONCESTATE_UNINITIALIZED,
     257              ("%d\n", iState));
    169258
    170259    /* Do the same as RTONCE_INITIALIZER does. */
    171260    ASMAtomicWriteS32(&pOnce->rc, VERR_INTERNAL_ERROR);
    172     ASMAtomicWriteS32(&pOnce->iState, -1);
     261    ASMAtomicWriteS32(&pOnce->iState, RTONCESTATE_UNINITIALIZED);
    173262}
    174263RT_EXPORT_SYMBOL(RTOnceReset);
Note: See TracChangeset for help on using the changeset viewer.

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