/*================================================================================================== CAMutex.cpp $Log: CAMutex.cpp,v $ Revision 1.7 2004/10/28 01:11:58 dwyatt [a radar for each of our projects] CAMutex.cpp (not .h) requires errno.h Revision 1.6 2004/09/29 00:59:10 jcm10 use the right test in Lock() Revision 1.5 2004/09/04 21:20:46 dwyatt add Log_LongLatencies Revision 1.4 2004/09/02 00:01:49 dwyatt optimize Lock() and Try() to call pthread_self once instead of twice Revision 1.3 2004/08/26 08:13:33 jcm10 finish bring up on Windows Revision 1.2 2004/03/13 02:27:38 jcm10 enable the new Try code Revision 1.1 2003/12/17 20:56:59 dwyatt new base class for CAGuard created Wed Dec 17 2003, Doug Wyatt Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved $NoKeywords: $ ==================================================================================================*/ //================================================================================================== // Includes //================================================================================================== // Self Include #include "CAMutex.h" #if TARGET_OS_MAC #include #endif // PublicUtility Includes #include "CADebugMacros.h" #include "CAException.h" #include "CAHostTimeBase.h" //================================================================================================== // Logging //================================================================================================== #if CoreAudio_Debug // #define Log_Ownership 1 // #define Log_Errors 1 // #define Log_LongLatencies 1 // #define LongLatencyThreshholdNS 1000000ULL // nanoseconds #endif //================================================================================================== // CAMutex //================================================================================================== CAMutex::CAMutex(const char* inName) : mName(inName), mOwner(0) { #if TARGET_OS_MAC OSStatus theError = pthread_mutex_init(&mMutex, NULL); ThrowIf(theError != 0, CAException(theError), "CAMutex::CAMutex: Could not init the mutex"); #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAMutex::CAMutex: creating %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner); #endif #elif TARGET_OS_WIN32 mMutex = CreateMutex(NULL, false, NULL); ThrowIfNULL(mMutex, CAException(GetLastError()), "CAMutex::CAMutex: could not create the mutex."); #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAMutex::CAMutex: creating %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner); #endif #endif } CAMutex::~CAMutex() { #if TARGET_OS_MAC #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAMutex::~CAMutex: destroying %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner); #endif pthread_mutex_destroy(&mMutex); #elif TARGET_OS_WIN32 #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAMutex::~CAMutex: destroying %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner); #endif if(mMutex != NULL) { CloseHandle(mMutex); } #endif } bool CAMutex::Lock() { bool theAnswer = false; #if TARGET_OS_MAC pthread_t theCurrentThread = pthread_self(); if(!pthread_equal(theCurrentThread, mOwner)) { #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAMutex::Lock: thread %p is locking %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner); #endif #if Log_LongLatencies UInt64 lockTryTime = CAHostTimeBase::GetCurrentTimeInNanos(); #endif OSStatus theError = pthread_mutex_lock(&mMutex); ThrowIf(theError != 0, CAException(theError), "CAMutex::Lock: Could not lock the mutex"); mOwner = theCurrentThread; theAnswer = true; #if Log_LongLatencies UInt64 lockAcquireTime = CAHostTimeBase::GetCurrentTimeInNanos(); if (lockAcquireTime - lockTryTime >= LongLatencyThresholdNS) DebugPrintfRtn(DebugPrintfFile, "Thread %p took %.6fs to acquire the lock %s\n", theCurrentThread, (lockAcquireTime - lockTryTime) * 1.0e-9 /* nanos to seconds */, mName); #endif #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAMutex::Lock: thread %p has locked %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner); #endif } #elif TARGET_OS_WIN32 if(mOwner != GetCurrentThreadId()) { #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAMutex::Lock: thread %lu is locking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif OSStatus theError = WaitForSingleObject(mMutex, INFINITE); ThrowIfError(theError, CAException(theError), "CAMutex::Lock: could not lock the mutex"); mOwner = GetCurrentThreadId(); theAnswer = true; #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAMutex::Lock: thread %lu has locked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif } #endif return theAnswer; } void CAMutex::Unlock() { #if TARGET_OS_MAC if(pthread_equal(pthread_self(), mOwner)) { #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAMutex::Unlock: thread %p is unlocking %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner); #endif mOwner = 0; OSStatus theError = pthread_mutex_unlock(&mMutex); ThrowIf(theError != 0, CAException(theError), "CAMutex::Unlock: Could not unlock the mutex"); #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAMutex::Unlock: thread %p has unlocked %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner); #endif } else { DebugMessage("CAMutex::Unlock: A thread is attempting to unlock a Mutex it doesn't own"); } #elif TARGET_OS_WIN32 if(mOwner == GetCurrentThreadId()) { #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAMutex::Unlock: thread %lu is unlocking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif mOwner = 0; bool wasReleased = ReleaseMutex(mMutex); ThrowIf(!wasReleased, CAException(GetLastError()), "CAMutex::Unlock: Could not unlock the mutex"); #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAMutex::Unlock: thread %lu has unlocked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif } else { DebugMessage("CAMutex::Unlock: A thread is attempting to unlock a Mutex it doesn't own"); } #endif } bool CAMutex::Try(bool& outWasLocked) { bool theAnswer = false; outWasLocked = false; #if TARGET_OS_MAC pthread_t theCurrentThread = pthread_self(); if(!pthread_equal(theCurrentThread, mOwner)) { // this means the current thread doesn't already own the lock #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAMutex::Try: thread %p is try-locking %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner); #endif // go ahead and call trylock to see if we can lock it. int theError = pthread_mutex_trylock(&mMutex); if(theError == 0) { // return value of 0 means we successfully locked the lock mOwner = theCurrentThread; theAnswer = true; outWasLocked = true; #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAMutex::Try: thread %p has locked %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner); #endif } else if(theError == EBUSY) { // return value of EBUSY means that the lock was already locked by another thread theAnswer = false; outWasLocked = false; #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAMutex::Try: thread %p failed to lock %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner); #endif } else { // any other return value means something really bad happenned ThrowIfError(theError, CAException(theError), "CAMutex::Try: call to pthread_mutex_trylock failed"); } } else { // this means the current thread already owns the lock theAnswer = true; outWasLocked = false; } #elif TARGET_OS_WIN32 if(mOwner != GetCurrentThreadId()) { // this means the current thread doesn't own the lock #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAMutex::Try: thread %lu is try-locking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif // try to acquire the mutex OSStatus theError = WaitForSingleObject(mMutex, 0); if(theError == WAIT_OBJECT_0) { // this means we successfully locked the lock mOwner = GetCurrentThreadId(); theAnswer = true; outWasLocked = true; #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAMutex::Try: thread %lu has locked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif } else if(theError == WAIT_TIMEOUT) { // this means that the lock was already locked by another thread theAnswer = false; outWasLocked = false; #if Log_Ownership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAMutex::Try: thread %lu failed to lock %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif } else { // any other return value means something really bad happenned ThrowIfError(theError, CAException(GetLastError()), "CAMutex::Try: call to lock the mutex failed"); } } else { // this means the current thread already owns the lock theAnswer = true; outWasLocked = false; } #endif return theAnswer; }