Changeset 33262 in vbox
- Timestamp:
- Oct 20, 2010 2:23:32 PM (15 years ago)
- svn:sync-xref-src-repo-rev:
- 66821
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/iprt/once.h
r28800 r33262 58 58 typedef RTONCE *PRTONCE; 59 59 60 /** 61 * The execute once statemachine. 62 */ 63 typedef 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 60 94 /** 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 } 62 96 63 97 -
trunk/src/VBox/Runtime/common/misc/once.cpp
r28800 r33262 39 39 40 40 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 */ 50 static 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 41 158 42 159 RTDECL(int) RTOnce(PRTONCE pOnce, PFNRTONCE pfnOnce, void *pvUser1, void *pvUser2) … … 52 169 */ 53 170 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 )) 55 175 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); 57 183 58 184 /* 59 185 * Do we initialize it? 60 186 */ 61 if (iState == -1) 187 int32_t rcOnce; 188 if ( iState == RTONCESTATE_UNINITIALIZED 189 && ASMAtomicCmpXchgS32(&pOnce->iState, RTONCESTATE_BUSY_NO_SEM, RTONCESTATE_UNINITIALIZED)) 62 190 { 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 ) 69 204 { 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 { 83 232 if (ASMAtomicDecS32(&pOnce->cEventRefs) == 0) 84 ASMAtomicWriteSize(&pOnce->hEventMulti, NIL_RTSEMEVENTMULTI);85 else86 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 indicates104 * that RTONCE::hEventMulti isn't valid either because it's not set105 * yet, or because it's being destroyed.106 */107 int32_t cEventRefs = ASMAtomicUoReadS32(&pOnce->cEventRefs);108 while ( cEventRefs > 0109 && !ASMAtomicCmpXchgS32(&pOnce->cEventRefs, cEventRefs + 1, cEventRefs))110 cEventRefs = ASMAtomicUoReadS32(&pOnce->cEventRefs);111 if (cEventRefs > 1)112 233 { 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); 151 238 } 152 239 } 240 if (RT_SUCCESS(rcOnce)) 241 rcOnce = ASMAtomicUoReadS32(&pOnce->rc); 153 242 } 154 243 155 /* 156 * Finally, return the status code from the execute once function. 157 */ 158 return ASMAtomicUoReadS32(&pOnce->rc); 244 return rcOnce; 159 245 } 160 246 RT_EXPORT_SYMBOL(RTOnce); … … 166 252 AssertPtr(pOnce); 167 253 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)); 169 258 170 259 /* Do the same as RTONCE_INITIALIZER does. */ 171 260 ASMAtomicWriteS32(&pOnce->rc, VERR_INTERNAL_ERROR); 172 ASMAtomicWriteS32(&pOnce->iState, -1);261 ASMAtomicWriteS32(&pOnce->iState, RTONCESTATE_UNINITIALIZED); 173 262 } 174 263 RT_EXPORT_SYMBOL(RTOnceReset);
Note:
See TracChangeset
for help on using the changeset viewer.