Changeset 33047 in vbox for trunk/src/VBox/Runtime/r0drv/darwin/semevent-r0drv-darwin.cpp
- Timestamp:
- Oct 11, 2010 6:59:23 PM (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/r0drv/darwin/semevent-r0drv-darwin.cpp
r29255 r33047 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Event Semaphores, Ring-0 Driver, Darwin.3 * IPRT - Single Release Event Semaphores, Ring-0 Driver, Darwin. 4 4 */ 5 5 … … 39 39 #endif 40 40 #include <iprt/err.h> 41 #include <iprt/list.h> 42 #include <iprt/lockvalidator.h> 41 43 #include <iprt/mem.h> 42 44 #include <iprt/mp.h> 43 45 #include <iprt/thread.h> 46 #include <iprt/time.h> 44 47 45 48 #include "internal/magics.h" … … 50 53 *******************************************************************************/ 51 54 /** 55 * Waiter entry. Lives on the stack. 56 */ 57 typedef 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. */ 65 typedef RTSEMEVENTDARWINENTRY *PRTSEMEVENTDARWINENTRY; 66 67 68 /** 52 69 * Darwin event semaphore. 53 70 */ … … 56 73 /** Magic value (RTSEMEVENT_MAGIC). */ 57 74 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; 60 79 /** Set if the event object is signaled. */ 61 uint8_t volatilefSignaled;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; 64 83 /** The spinlock protecting us. */ 65 84 lck_spin_t *pSpinlock; … … 85 104 { 86 105 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); 90 110 Assert(g_pDarwinLockGroup); 91 111 pThis->pSpinlock = lck_spin_alloc_init(g_pDarwinLockGroup, LCK_ATTR_NULL); … … 102 122 } 103 123 124 125 /** 126 * Retain a reference to the semaphore. 127 * 128 * @param pThis The semaphore. 129 */ 130 DECLINLINE(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 */ 142 DECLINLINE(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 } 104 151 105 152 RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem) … … 113 160 114 161 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); 132 176 133 177 return VINF_SUCCESS; … … 145 189 RT_ASSERT_INTS_ON(); 146 190 191 rtR0SemEventDarwinRetain(pThis); 192 147 193 /** @todo should probably disable interrupts here... update 148 194 * semspinmutex-r0drv-generic.c when done. */ 149 195 lck_spin_lock(pThis->pSpinlock); 150 196 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 } 163 213 164 214 lck_spin_unlock(pThis->pSpinlock); 215 rtR0SemEventDarwinRelease(pThis); 165 216 166 217 RT_ASSERT_PREEMPT_CPUID(); … … 169 220 170 221 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 */ 231 static 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); 179 242 lck_spin_lock(pThis->pSpinlock); 180 243 244 /* 245 * In the signaled state? 246 */ 181 247 int rc; 182 if (pThis->fSignaled) 183 { 184 Assert(!pThis->cWaiters); 185 ASMAtomicXchgU8(&pThis->fSignaled, false); 248 if (ASMAtomicCmpXchgBool(&pThis->fSignaled, false, true)) 186 249 rc = VINF_SUCCESS; 187 }188 else if (!cMillies)189 rc = VERR_TIMEOUT;190 250 else 191 251 { 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 } 197 296 else 198 297 { 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 212 313 { 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); 219 318 } 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; 221 359 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); 253 363 } 254 364 } 255 365 256 366 lck_spin_unlock(pThis->pSpinlock); 367 rtR0SemEventDarwinRelease(pThis); 257 368 return rc; 258 369 } 259 370 260 371 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 373 RTDECL(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 384 RTDECL(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.