PublicUtility/CAGuard.cpp
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
Part of Core Audio Public Utility Classes |
*/ |
//================================================================================================== |
// Includes |
//================================================================================================== |
// Self Include |
#include "CAGuard.h" |
#if TARGET_OS_MAC |
#include <errno.h> |
#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 |
DebugPrintf("%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 |
DebugPrintf("%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 |
DebugPrintf("%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(); |
ResetEvent(mEvent); |
#if Log_WaitOwnership |
DebugPrintf("%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 = static_cast<long>(inNanos / kNanosPerSecond); |
theTimeSpec.tv_nsec = static_cast<long>(inNanos % kNanosPerSecond); |
} |
else |
{ |
theTimeSpec.tv_sec = 0; |
theTimeSpec.tv_nsec = static_cast<long>(inNanos); |
} |
#if Log_TimedWaits || Log_Latency || Log_Average_Latency |
UInt64 theStartNanos = CAHostTimeBase::GetCurrentTimeInNanos(); |
#endif |
mOwner = 0; |
#if Log_WaitOwnership |
DebugPrintf("%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 |
DebugPrintf("%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<UInt32>(inNanos / 1000000ULL); |
#if Log_TimedWaits || Log_Latency || Log_Average_Latency |
UInt64 theStartNanos = CAHostTimeBase::GetCurrentTimeInNanos(); |
#endif |
mOwner = 0; |
#if Log_WaitOwnership |
DebugPrintf("%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(); |
ResetEvent(mEvent); |
// This mutex should be locked again when time out happens.rdar:// 12270555 |
if(theError == WAIT_TIMEOUT) { |
DWORD dwError = WaitForSingleObject(mMutex, INFINITE); |
ThrowIf((dwError != WAIT_OBJECT_0), CAException(GetLastError()), "CAGuard::WaitFor: failed to acquire the mutex back when timeout happened\n"); |
} |
#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 |
DebugPrintf("%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); |
} |
else |
{ |
#if Log_Errors |
DebugMessageN2("CAGuard::WaitUntil: Time has expired before waiting, now: %.0f, requested: %.0f", (double)theCurrentNanos, (double)inNanos); |
#endif |
theAnswer = true; |
} |
return theAnswer; |
} |
void CAGuard::Notify() |
{ |
#if TARGET_OS_MAC |
#if Log_WaitOwnership |
DebugPrintf("%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 |
DebugPrintf("%lu %.4f: CAGuard::Notify: thread %lu is notifying %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); |
#endif |
SetEvent(mEvent); |
#endif |
} |
void CAGuard::NotifyAll() |
{ |
#if TARGET_OS_MAC |
#if Log_WaitOwnership |
DebugPrintf("%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 |
DebugPrintf("%lu %.4f: CAGuard::NotifyAll: thread %lu is notifying %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); |
#endif |
SetEvent(mEvent); |
#endif |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-02-19