/*================================================================================================== CAGuard.cpp $Log: CAGuard.cpp,v $ Revision 1.16 2004/11/08 21:15:50 luke add #include per Doug's request Revision 1.15 2004/08/26 08:13:33 jcm10 finish bring up on Windows Revision 1.14 2004/01/07 22:30:19 dwyatt fix file name in header Revision 1.13 2003/12/17 21:00:16 dwyatt derive from CAMutex Revision 1.12 2003/12/17 19:49:15 dwyatt Windows conditionalizing Revision 1.11 2003/12/17 18:58:08 dwyatt renamed from CAGuard.cp Revision 1.10 2003/09/12 01:18:11 jcm10 put in new code for Try(), but have it ifdef'd out for now Revision 1.9 2003/08/05 01:11:15 jcm10 use pthread_equal Revision 1.8 2003/04/10 00:01:53 jcm10 turn off logging that might happen on the IO thread Revision 1.7 2002/07/07 00:46:59 jcm10 comment out the warning about the try-based Locker Revision 1.6 2002/06/13 06:31:47 dwyatt fix compile error :-) Revision 1.5 2002/06/13 04:28:55 bills fix compile error Revision 1.4 2002/04/09 07:57:55 bills fix try [jcm10] Revision 1.3 2002/04/05 23:10:43 jcm10 move the warning to the .cp file Revision 1.2 2002/04/02 20:42:07 bills add Try Revision 1.1 2002/03/01 01:52:40 jcm10 moved here from ../Utility Revision 1.17 2002/02/28 23:24:29 jcm10 added the CA prefix to DebugMacros and LogMacros for more consistency Revision 1.16 2001/11/15 02:07:24 jcm10 remove extraneous include of Revision 1.15 2001/07/04 02:00:26 jcm10 give CAGuard a name for easier debugging Revision 1.14 2001/01/19 00:48:24 jcm10 add more logging Revision 1.13 2001/01/18 02:50:51 jcm10 use CoreAudio_Debug instead of DEBUG Revision 1.12 2000/12/10 22:20:08 jcm10 more fixes for new driver model Revision 1.11 2000/12/07 02:18:40 jcm10 turn off the logging Revision 1.10 2000/11/21 20:45:50 jcm10 more XFiles fun Revision 1.9 2000/11/21 19:48:16 jcm10 turn off the logging Revision 1.8 2000/10/12 00:18:11 jcm10 use CAHostTimeBase::GetCurrentTimeInNanos() Revision 1.7 2000/10/04 19:58:55 jcm10 Add conversion routines to CAHostTimeBase Revision 1.6 2000/10/02 18:34:29 jcm10 XFiles work better Revision 1.5 2000/09/25 23:15:31 jcm10 XFiles now work (almost) Revision 1.4 2000/09/24 01:12:32 jcm10 first checked in Revision 1.3 2000/09/13 02:28:49 jcm10 make it build again Revision 1.2 2000/08/25 01:07:26 jcm10 update things to build the XFiles and prune the CoreAudio target to just the code necessary to run it Revision 1.1 2000/08/24 23:36:46 jcm10 First checked in Revision 0.0 2000/01/01 12:34:56 jcm10 created $NoKeywords: $ ==================================================================================================*/ //================================================================================================== // Includes //================================================================================================== // Self Include #include "CAGuard.h" #if TARGET_OS_MAC #include #endif // PublicUtility Inludes #include "CADebugMacros.h" #include "CAException.h" #include "CAHostTimeBase.h" //================================================================================================== // Logging //================================================================================================== #if CoreAudio_Debug // #define Log_Ownership 1 // #define Log_WaitOwnership 1 // #define Log_TimedWaits 1 // #define Log_Latency 1 // #define Log_Errors 1 #endif //#warning Need a try-based Locker too //================================================================================================== // CAGuard //================================================================================================== CAGuard::CAGuard(const char* inName) : CAMutex(inName) #if Log_Average_Latency ,mAverageLatencyAccumulator(0.0), mAverageLatencyCount(0) #endif { #if TARGET_OS_MAC OSStatus theError = pthread_cond_init(&mCondVar, NULL); ThrowIf(theError != 0, CAException(theError), "CAGuard::CAGuard: Could not init the cond var"); #elif TARGET_OS_WIN32 mEvent = CreateEvent(NULL, true, false, NULL); ThrowIfNULL(mEvent, CAException(GetLastError()), "CAGuard::CAGuard: Could not create the event"); #endif } CAGuard::~CAGuard() { #if TARGET_OS_MAC pthread_cond_destroy(&mCondVar); #elif TARGET_OS_WIN32 if(mEvent != NULL) { CloseHandle(mEvent); } #endif } void CAGuard::Wait() { #if TARGET_OS_MAC ThrowIf(!pthread_equal(pthread_self(), mOwner), CAException(1), "CAGuard::Wait: A thread has to have locked a guard before it can wait"); mOwner = 0; #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAGuard::Wait: thread %p is waiting on %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner); #endif OSStatus theError = pthread_cond_wait(&mCondVar, &mMutex); ThrowIf(theError != 0, CAException(theError), "CAGuard::Wait: Could not wait for a signal"); mOwner = pthread_self(); #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAGuard::Wait: thread %p waited on %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner); #endif #elif TARGET_OS_WIN32 ThrowIf(GetCurrentThreadId() != mOwner, CAException(1), "CAGuard::Wait: A thread has to have locked a guard before it can wait"); mOwner = 0; #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAGuard::Wait: thread %lu is waiting on %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif ReleaseMutex(mMutex); HANDLE theHandles[] = { mMutex, mEvent }; OSStatus theError = WaitForMultipleObjects(2, theHandles, true, INFINITE); ThrowIfError(theError, CAException(GetLastError()), "CAGuard::Wait: Could not wait for the signal"); mOwner = GetCurrentThreadId(); #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAGuard::Wait: thread %lu waited on %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif #endif } bool CAGuard::WaitFor(UInt64 inNanos) { bool theAnswer = false; #if TARGET_OS_MAC ThrowIf(!pthread_equal(pthread_self(), mOwner), CAException(1), "CAGuard::WaitFor: A thread has to have locked a guard be for it can wait"); #if Log_TimedWaits DebugMessageN1("CAGuard::WaitFor: waiting %.0f", (Float64)inNanos); #endif struct timespec theTimeSpec; static const UInt64 kNanosPerSecond = 1000000000ULL; if(inNanos > kNanosPerSecond) { theTimeSpec.tv_sec = inNanos / kNanosPerSecond; theTimeSpec.tv_nsec = inNanos % kNanosPerSecond; } else { theTimeSpec.tv_sec = 0; theTimeSpec.tv_nsec = inNanos; } #if Log_TimedWaits || Log_Latency || Log_Average_Latency UInt64 theStartNanos = CAHostTimeBase::GetCurrentTimeInNanos(); #endif mOwner = 0; #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAGuard::WaitFor: thread %p is waiting on %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner); #endif OSStatus theError = pthread_cond_timedwait_relative_np(&mCondVar, &mMutex, &theTimeSpec); ThrowIf((theError != 0) && (theError != ETIMEDOUT), CAException(theError), "CAGuard::WaitFor: Wait got an error"); mOwner = pthread_self(); #if Log_TimedWaits || Log_Latency || Log_Average_Latency UInt64 theEndNanos = CAHostTimeBase::GetCurrentTimeInNanos(); #endif #if Log_TimedWaits DebugMessageN1("CAGuard::WaitFor: waited %.0f", (Float64)(theEndNanos - theStartNanos)); #endif #if Log_Latency DebugMessageN1("CAGuard::WaitFor: latency %.0f", (Float64)((theEndNanos - theStartNanos) - inNanos)); #endif #if Log_Average_Latency ++mAverageLatencyCount; mAverageLatencyAccumulator += (theEndNanos - theStartNanos) - inNanos; if(mAverageLatencyCount >= 50) { DebugMessageN2("CAGuard::WaitFor: average latency %.3f ns over %ld waits", mAverageLatencyAccumulator / mAverageLatencyCount, mAverageLatencyCount); mAverageLatencyCount = 0; mAverageLatencyAccumulator = 0.0; } #endif #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAGuard::WaitFor: thread %p waited on %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner); #endif theAnswer = theError == ETIMEDOUT; #elif TARGET_OS_WIN32 ThrowIf(GetCurrentThreadId() != mOwner, CAException(1), "CAGuard::WaitFor: A thread has to have locked a guard be for it can wait"); #if Log_TimedWaits DebugMessageN1("CAGuard::WaitFor: waiting %.0f", (Float64)inNanos); #endif // the time out is specified in milliseconds(!) UInt32 theWaitTime = static_cast(inNanos / 1000000ULL); #if Log_TimedWaits || Log_Latency || Log_Average_Latency UInt64 theStartNanos = CAHostTimeBase::GetCurrentTimeInNanos(); #endif mOwner = 0; #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAGuard::WaitFor: thread %lu is waiting on %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif ReleaseMutex(mMutex); HANDLE theHandles[] = { mMutex, mEvent }; OSStatus theError = WaitForMultipleObjects(2, theHandles, true, theWaitTime); ThrowIf((theError != WAIT_OBJECT_0) && (theError != WAIT_TIMEOUT), CAException(GetLastError()), "CAGuard::WaitFor: Wait got an error"); mOwner = GetCurrentThreadId(); #if Log_TimedWaits || Log_Latency || Log_Average_Latency UInt64 theEndNanos = CAHostTimeBase::GetCurrentTimeInNanos(); #endif #if Log_TimedWaits DebugMessageN1("CAGuard::WaitFor: waited %.0f", (Float64)(theEndNanos - theStartNanos)); #endif #if Log_Latency DebugMessageN1("CAGuard::WaitFor: latency %.0f", (Float64)((theEndNanos - theStartNanos) - inNanos)); #endif #if Log_Average_Latency ++mAverageLatencyCount; mAverageLatencyAccumulator += (theEndNanos - theStartNanos) - inNanos; if(mAverageLatencyCount >= 50) { DebugMessageN2("CAGuard::WaitFor: average latency %.3f ns over %ld waits", mAverageLatencyAccumulator / mAverageLatencyCount, mAverageLatencyCount); mAverageLatencyCount = 0; mAverageLatencyAccumulator = 0.0; } #endif #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAGuard::WaitFor: thread %lu waited on %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif theAnswer = theError == WAIT_TIMEOUT; #endif return theAnswer; } bool CAGuard::WaitUntil(UInt64 inNanos) { bool theAnswer = false; UInt64 theCurrentNanos = CAHostTimeBase::GetCurrentTimeInNanos(); #if Log_TimedWaits DebugMessageN2("CAGuard::WaitUntil: now: %.0f, requested: %.0f", (double)theCurrentNanos, (double)inNanos); #endif if(inNanos > theCurrentNanos) { #if Log_Errors if((inNanos - theCurrentNanos) > 1000000000ULL) { DebugMessage("CAGuard::WaitUntil: about to wait for more than a second"); } #endif theAnswer = WaitFor(inNanos - theCurrentNanos); } #if Log_Errors else { DebugMessageN2("CAGuard::WaitUntil: Time has expired before waiting, now: %.0f, requested: %.0f", (double)theCurrentNanos, (double)inNanos); } #endif return theAnswer; } void CAGuard::Notify() { #if TARGET_OS_MAC #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAGuard::Notify: thread %p is notifying %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner); #endif OSStatus theError = pthread_cond_signal(&mCondVar); ThrowIf(theError != 0, CAException(theError), "CAGuard::Notify: failed"); #elif TARGET_OS_WIN32 #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAGuard::Notify: thread %lu is notifying %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif PulseEvent(mEvent); #endif } void CAGuard::NotifyAll() { #if TARGET_OS_MAC #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%p %.4f: CAGuard::NotifyAll: thread %p is notifying %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner); #endif OSStatus theError = pthread_cond_broadcast(&mCondVar); ThrowIf(theError != 0, CAException(theError), "CAGuard::NotifyAll: failed"); #elif TARGET_OS_WIN32 #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFile, "%lu %.4f: CAGuard::NotifyAll: thread %lu is notifying %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif PulseEvent(mEvent); #endif }