/* $Id: critsect-generic.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */ /** @file * IPRT - Critical Section, Generic. */ /* * Copyright (C) 2006-2009 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the * VirtualBox OSE distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #include "internal/iprt.h" #include #include #include #include #include #include "internal/thread.h" #include "internal/strict.h" #undef RTCritSectInit RTDECL(int) RTCritSectInit(PRTCRITSECT pCritSect) { return RTCritSectInitEx(pCritSect, 0, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "RTCritSect"); } RT_EXPORT_SYMBOL(RTCritSectInit); RTDECL(int) RTCritSectInitEx(PRTCRITSECT pCritSect, uint32_t fFlags, RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...) { AssertReturn(fFlags <= (RTCRITSECT_FLAGS_NO_NESTING | RTCRITSECT_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER); /* * Initialize the structure and */ pCritSect->u32Magic = RTCRITSECT_MAGIC; pCritSect->fFlags = fFlags; pCritSect->cNestings = 0; pCritSect->cLockers = -1; pCritSect->NativeThreadOwner = NIL_RTNATIVETHREAD; pCritSect->pValidatorRec = NULL; int rc = VINF_SUCCESS; #ifdef RTCRITSECT_STRICT if (!pszNameFmt) { static uint32_t volatile s_iCritSectAnon = 0; rc = RTLockValidatorRecExclCreate(&pCritSect->pValidatorRec, hClass, uSubClass, pCritSect, !(fFlags & RTCRITSECT_FLAGS_NO_LOCK_VAL), "RTCritSect-%u", ASMAtomicIncU32(&s_iCritSectAnon) - 1); } else { va_list va; va_start(va, pszNameFmt); rc = RTLockValidatorRecExclCreateV(&pCritSect->pValidatorRec, hClass, uSubClass, pCritSect, !(fFlags & RTCRITSECT_FLAGS_NO_LOCK_VAL), pszNameFmt, va); va_end(va); } #endif if (RT_SUCCESS(rc)) { rc = RTSemEventCreate(&pCritSect->EventSem); if (RT_SUCCESS(rc)) return VINF_SUCCESS; RTLockValidatorRecExclDestroy(&pCritSect->pValidatorRec); } AssertRC(rc); pCritSect->EventSem = NULL; pCritSect->u32Magic = (uint32_t)rc; return rc; } RT_EXPORT_SYMBOL(RTCritSectInitEx); RTDECL(uint32_t) RTCritSectSetSubClass(PRTCRITSECT pCritSect, uint32_t uSubClass) { #ifdef RTCRITSECT_STRICT AssertPtrReturn(pCritSect, RTLOCKVAL_SUB_CLASS_INVALID); AssertReturn(pCritSect->u32Magic == RTCRITSECT_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID); return RTLockValidatorRecExclSetSubClass(pCritSect->pValidatorRec, uSubClass); #else return RTLOCKVAL_SUB_CLASS_INVALID; #endif } DECL_FORCE_INLINE(int) rtCritSectTryEnter(PRTCRITSECT pCritSect, PCRTLOCKVALSRCPOS pSrcPos) { Assert(pCritSect); Assert(pCritSect->u32Magic == RTCRITSECT_MAGIC); RTNATIVETHREAD NativeThreadSelf = RTThreadNativeSelf(); /* * Try take the lock. (cLockers is -1 if it's free) */ if (!ASMAtomicCmpXchgS32(&pCritSect->cLockers, 0, -1)) { /* * Somebody is owning it (or will be soon). Perhaps it's us? */ if (pCritSect->NativeThreadOwner == NativeThreadSelf) { if (!(pCritSect->fFlags & RTCRITSECT_FLAGS_NO_NESTING)) { #ifdef RTCRITSECT_STRICT int rc9 = RTLockValidatorRecExclRecursion(pCritSect->pValidatorRec, pSrcPos); if (RT_FAILURE(rc9)) return rc9; #endif ASMAtomicIncS32(&pCritSect->cLockers); pCritSect->cNestings++; return VINF_SUCCESS; } AssertMsgFailed(("Nested entry of critsect %p\n", pCritSect)); return VERR_SEM_NESTED; } return VERR_SEM_BUSY; } /* * First time */ pCritSect->cNestings = 1; ASMAtomicWriteHandle(&pCritSect->NativeThreadOwner, NativeThreadSelf); #ifdef RTCRITSECT_STRICT RTLockValidatorRecExclSetOwner(pCritSect->pValidatorRec, NIL_RTTHREAD, pSrcPos, true); #endif return VINF_SUCCESS; } #undef RTCritSectTryEnter RTDECL(int) RTCritSectTryEnter(PRTCRITSECT pCritSect) { #ifndef RTCRTISECT_STRICT return rtCritSectTryEnter(pCritSect, NULL); #else RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API(); return rtCritSectTryEnter(pCritSect, &SrcPos); #endif } RT_EXPORT_SYMBOL(RTCritSectTryEnter); RTDECL(int) RTCritSectTryEnterDebug(PRTCRITSECT pCritSect, RTHCUINTPTR uId, RT_SRC_POS_DECL) { RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); return rtCritSectTryEnter(pCritSect, &SrcPos); } RT_EXPORT_SYMBOL(RTCritSectTryEnterDebug); DECL_FORCE_INLINE(int) rtCritSectEnter(PRTCRITSECT pCritSect, PCRTLOCKVALSRCPOS pSrcPos) { Assert(pCritSect); Assert(pCritSect->u32Magic == RTCRITSECT_MAGIC); RTNATIVETHREAD NativeThreadSelf = RTThreadNativeSelf(); /* If the critical section has already been destroyed, then inform the caller. */ if (pCritSect->u32Magic != RTCRITSECT_MAGIC) return VERR_SEM_DESTROYED; #ifdef RTCRITSECT_STRICT RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt(); int rc9 = RTLockValidatorRecExclCheckOrder(pCritSect->pValidatorRec, hThreadSelf, pSrcPos, RT_INDEFINITE_WAIT); if (RT_FAILURE(rc9)) return rc9; #endif /* * Increment the waiter counter. * This becomes 0 when the section is free. */ if (ASMAtomicIncS32(&pCritSect->cLockers) > 0) { /* * Nested? */ if (pCritSect->NativeThreadOwner == NativeThreadSelf) { if (!(pCritSect->fFlags & RTCRITSECT_FLAGS_NO_NESTING)) { #ifdef RTCRITSECT_STRICT rc9 = RTLockValidatorRecExclRecursion(pCritSect->pValidatorRec, pSrcPos); if (RT_FAILURE(rc9)) { ASMAtomicDecS32(&pCritSect->cLockers); return rc9; } #endif pCritSect->cNestings++; return VINF_SUCCESS; } AssertBreakpoint(); /* don't do normal assertion here, the logger uses this code too. */ ASMAtomicDecS32(&pCritSect->cLockers); return VERR_SEM_NESTED; } /* * Wait for the current owner to release it. */ #ifndef RTCRITSECT_STRICT RTTHREAD hThreadSelf = RTThreadSelf(); #endif for (;;) { #ifdef RTCRITSECT_STRICT rc9 = RTLockValidatorRecExclCheckBlocking(pCritSect->pValidatorRec, hThreadSelf, pSrcPos, !(pCritSect->fFlags & RTCRITSECT_FLAGS_NO_NESTING), RT_INDEFINITE_WAIT, RTTHREADSTATE_CRITSECT, false); if (RT_FAILURE(rc9)) { ASMAtomicDecS32(&pCritSect->cLockers); return rc9; } #else RTThreadBlocking(hThreadSelf, RTTHREADSTATE_CRITSECT, false); #endif int rc = RTSemEventWait(pCritSect->EventSem, RT_INDEFINITE_WAIT); RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_CRITSECT); if (pCritSect->u32Magic != RTCRITSECT_MAGIC) return VERR_SEM_DESTROYED; if (rc == VINF_SUCCESS) break; AssertMsg(rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED, ("rc=%Rrc\n", rc)); } AssertMsg(pCritSect->NativeThreadOwner == NIL_RTNATIVETHREAD, ("pCritSect->NativeThreadOwner=%p\n", pCritSect->NativeThreadOwner)); } /* * First time */ pCritSect->cNestings = 1; ASMAtomicWriteHandle(&pCritSect->NativeThreadOwner, NativeThreadSelf); #ifdef RTCRITSECT_STRICT RTLockValidatorRecExclSetOwner(pCritSect->pValidatorRec, hThreadSelf, pSrcPos, true); #endif return VINF_SUCCESS; } #undef RTCritSectEnter RTDECL(int) RTCritSectEnter(PRTCRITSECT pCritSect) { #ifndef RTCRITSECT_STRICT return rtCritSectEnter(pCritSect, NULL); #else RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API(); return rtCritSectEnter(pCritSect, &SrcPos); #endif } RT_EXPORT_SYMBOL(RTCritSectEnter); RTDECL(int) RTCritSectEnterDebug(PRTCRITSECT pCritSect, RTHCUINTPTR uId, RT_SRC_POS_DECL) { RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); return rtCritSectEnter(pCritSect, &SrcPos); } RT_EXPORT_SYMBOL(RTCritSectEnterDebug); RTDECL(int) RTCritSectLeave(PRTCRITSECT pCritSect) { /* * Assert ownership and so on. */ Assert(pCritSect); Assert(pCritSect->u32Magic == RTCRITSECT_MAGIC); Assert(pCritSect->cNestings > 0); Assert(pCritSect->cLockers >= 0); Assert(pCritSect->NativeThreadOwner == RTThreadNativeSelf()); #ifdef RTCRITSECT_STRICT int rc9 = RTLockValidatorRecExclReleaseOwner(pCritSect->pValidatorRec, pCritSect->cNestings == 1); if (RT_FAILURE(rc9)) return rc9; #endif /* * Decrement nestings, if <= 0 when we'll release the critsec. */ pCritSect->cNestings--; if (pCritSect->cNestings > 0) ASMAtomicDecS32(&pCritSect->cLockers); else { /* * Set owner to zero. * Decrement waiters, if >= 0 then we have to wake one of them up. */ ASMAtomicWriteHandle(&pCritSect->NativeThreadOwner, NIL_RTNATIVETHREAD); if (ASMAtomicDecS32(&pCritSect->cLockers) >= 0) { int rc = RTSemEventSignal(pCritSect->EventSem); AssertReleaseMsg(RT_SUCCESS(rc), ("RTSemEventSignal -> %Rrc\n", rc)); } } return VINF_SUCCESS; } RT_EXPORT_SYMBOL(RTCritSectLeave); static int rtCritSectEnterMultiple(size_t cCritSects, PRTCRITSECT *papCritSects, PCRTLOCKVALSRCPOS pSrcPos) { Assert(cCritSects > 0); AssertPtr(papCritSects); /* * Try get them all. */ int rc = VERR_INVALID_PARAMETER; size_t i; for (i = 0; i < cCritSects; i++) { rc = rtCritSectTryEnter(papCritSects[i], pSrcPos); if (RT_FAILURE(rc)) break; } if (RT_SUCCESS(rc)) return rc; /* * The retry loop. */ for (unsigned cTries = 0; ; cTries++) { /* * We've failed, release any locks we might have gotten. ('i' is the lock that failed btw.) */ size_t j = i; while (j-- > 0) { int rc2 = RTCritSectLeave(papCritSects[j]); AssertRC(rc2); } if (rc != VERR_SEM_BUSY) return rc; /* * Try prevent any theoretical synchronous races with other threads. */ Assert(cTries < 1000000); if (cTries > 10000) RTThreadSleep(cTries % 3); /* * Wait on the one we failed to get. */ rc = rtCritSectEnter(papCritSects[i], pSrcPos); if (RT_FAILURE(rc)) return rc; /* * Try take the others. */ for (j = 0; j < cCritSects; j++) { if (j != i) { rc = rtCritSectTryEnter(papCritSects[j], pSrcPos); if (RT_FAILURE(rc)) break; } } if (RT_SUCCESS(rc)) return rc; /* * We failed. */ if (i > j) { int rc2 = RTCritSectLeave(papCritSects[i]); AssertRC(rc2); } i = j; } } #undef RTCritSectEnterMultiple RTDECL(int) RTCritSectEnterMultiple(size_t cCritSects, PRTCRITSECT *papCritSects) { #ifndef RTCRITSECT_STRICT return rtCritSectEnterMultiple(cCritSects, papCritSects, NULL); #else RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API(); return rtCritSectEnterMultiple(cCritSects, papCritSects, &SrcPos); #endif } RT_EXPORT_SYMBOL(RTCritSectEnterMultiple); RTDECL(int) RTCritSectEnterMultipleDebug(size_t cCritSects, PRTCRITSECT *papCritSects, RTUINTPTR uId, RT_SRC_POS_DECL) { RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); return rtCritSectEnterMultiple(cCritSects, papCritSects, &SrcPos); } RT_EXPORT_SYMBOL(RTCritSectEnterMultipleDebug); RTDECL(int) RTCritSectLeaveMultiple(size_t cCritSects, PRTCRITSECT *papCritSects) { int rc = VINF_SUCCESS; for (size_t i = 0; i < cCritSects; i++) { int rc2 = RTCritSectLeave(papCritSects[i]); if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) rc = rc2; } return rc; } RT_EXPORT_SYMBOL(RTCritSectLeaveMultiple); RTDECL(int) RTCritSectDelete(PRTCRITSECT pCritSect) { /* * Assert free waiters and so on. */ Assert(pCritSect); Assert(pCritSect->u32Magic == RTCRITSECT_MAGIC); Assert(pCritSect->cNestings == 0); Assert(pCritSect->cLockers == -1); Assert(pCritSect->NativeThreadOwner == NIL_RTNATIVETHREAD); /* * Invalidate the structure and free the mutex. * In case someone is waiting we'll signal the semaphore cLockers + 1 times. */ ASMAtomicWriteU32(&pCritSect->u32Magic, ~RTCRITSECT_MAGIC); pCritSect->fFlags = 0; pCritSect->cNestings = 0; pCritSect->NativeThreadOwner= NIL_RTNATIVETHREAD; RTSEMEVENT EventSem = pCritSect->EventSem; pCritSect->EventSem = NIL_RTSEMEVENT; while (pCritSect->cLockers-- >= 0) RTSemEventSignal(EventSem); ASMAtomicWriteS32(&pCritSect->cLockers, -1); int rc = RTSemEventDestroy(EventSem); AssertRC(rc); RTLockValidatorRecExclDestroy(&pCritSect->pValidatorRec); return rc; } RT_EXPORT_SYMBOL(RTCritSectDelete);