/*****************************************************************************/ // Copyright 2006-2008 Adobe Systems Incorporated // All Rights Reserved. // // NOTICE: Adobe permits you to use, modify, and distribute this file in // accordance with the terms of the Adobe license agreement accompanying it. /*****************************************************************************/ /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_mutex.cpp#3 $ */ /* $DateTime: 2012/09/05 12:31:51 $ */ /* $Change: 847652 $ */ /* $Author: tknoll $ */ #include "dng_mutex.h" #include "dng_assertions.h" #include "dng_exceptions.h" #include <stdlib.h> /*****************************************************************************/ #if qDNGThreadSafe namespace { class InnermostMutexHolder { private: pthread_key_t fInnermostMutexKey; public: InnermostMutexHolder () : fInnermostMutexKey () { int result = pthread_key_create (&fInnermostMutexKey, NULL); DNG_ASSERT (result == 0, "pthread_key_create failed."); if (result != 0) ThrowProgramError (); } ~InnermostMutexHolder () { pthread_key_delete (fInnermostMutexKey); } void SetInnermostMutex (dng_mutex *mutex) { int result; result = pthread_setspecific (fInnermostMutexKey, (void *)mutex); DNG_ASSERT (result == 0, "pthread_setspecific failed."); #if 0 // Hard failure here was causing crash on quit. if (result != 0) ThrowProgramError (); #endif } dng_mutex *GetInnermostMutex () { void *result = pthread_getspecific (fInnermostMutexKey); return reinterpret_cast<dng_mutex *> (result); } }; InnermostMutexHolder gInnermostMutexHolder; } #endif /*****************************************************************************/ dng_mutex::dng_mutex (const char *mutexName, uint32 mutexLevel) #if qDNGThreadSafe : fPthreadMutex () , fMutexLevel (mutexLevel) , fRecursiveLockCount (0) , fPrevHeldMutex (NULL) , fMutexName (mutexName) #endif { #if qDNGThreadSafe if (pthread_mutex_init (&fPthreadMutex, NULL) != 0) { ThrowMemoryFull (); } #endif } /*****************************************************************************/ dng_mutex::~dng_mutex () { #if qDNGThreadSafe pthread_mutex_destroy (&fPthreadMutex); #endif } /*****************************************************************************/ void dng_mutex::Lock () { #if qDNGThreadSafe dng_mutex *innermostMutex = gInnermostMutexHolder.GetInnermostMutex (); if (innermostMutex != NULL) { if (innermostMutex == this) { fRecursiveLockCount++; return; } bool lockOrderPreserved = fMutexLevel > innermostMutex->fMutexLevel /* || (fMutexLevel == innermostMutex->fMutexLevel && innermostMutex < this) */; if (!lockOrderPreserved) { DNG_REPORT ("Lock ordering violation."); #if qDNGDebug dng_show_message_f ("This mutex: %s v Innermost mutex: %s", this->MutexName (), innermostMutex->MutexName ()); #endif } } pthread_mutex_lock (&fPthreadMutex); fPrevHeldMutex = innermostMutex; gInnermostMutexHolder.SetInnermostMutex (this); #endif } /*****************************************************************************/ void dng_mutex::Unlock () { #if qDNGThreadSafe DNG_ASSERT (gInnermostMutexHolder.GetInnermostMutex () == this, "Mutexes unlocked out of order!!!"); if (fRecursiveLockCount > 0) { fRecursiveLockCount--; return; } gInnermostMutexHolder.SetInnermostMutex (fPrevHeldMutex); fPrevHeldMutex = NULL; pthread_mutex_unlock (&fPthreadMutex); #endif } /*****************************************************************************/ const char *dng_mutex::MutexName () const { #if qDNGThreadSafe if (fMutexName) return fMutexName; #endif return "< unknown >"; } /*****************************************************************************/ dng_lock_mutex::dng_lock_mutex (dng_mutex *mutex) : fMutex (mutex) { if (fMutex) fMutex->Lock (); } /*****************************************************************************/ dng_lock_mutex::~dng_lock_mutex () { if (fMutex) fMutex->Unlock (); } /*****************************************************************************/ dng_unlock_mutex::dng_unlock_mutex (dng_mutex *mutex) : fMutex (mutex) { if (fMutex) fMutex->Unlock (); } /*****************************************************************************/ dng_unlock_mutex::~dng_unlock_mutex () { if (fMutex) fMutex->Lock (); } /*****************************************************************************/ #if qDNGThreadSafe /*****************************************************************************/ dng_condition::dng_condition () : fPthreadCondition () { int result; result = pthread_cond_init (&fPthreadCondition, NULL); DNG_ASSERT (result == 0, "pthread_cond_init failed."); if (result != 0) { ThrowProgramError (); } } /*****************************************************************************/ dng_condition::~dng_condition () { pthread_cond_destroy (&fPthreadCondition); } /*****************************************************************************/ bool dng_condition::Wait (dng_mutex &mutex, double timeoutSecs) { bool timedOut = false; dng_mutex *innermostMutex = gInnermostMutexHolder.GetInnermostMutex (); DNG_ASSERT (innermostMutex == &mutex, "Attempt to wait on non-innermost mutex."); innermostMutex = mutex.fPrevHeldMutex; gInnermostMutexHolder.SetInnermostMutex (innermostMutex); mutex.fPrevHeldMutex = NULL; if (timeoutSecs < 0) { pthread_cond_wait (&fPthreadCondition, &mutex.fPthreadMutex); } else { struct timespec now; dng_pthread_now (&now); timeoutSecs += now.tv_sec; timeoutSecs += now.tv_nsec / 1000000000.0; now.tv_sec = (long) timeoutSecs; now.tv_nsec = (long) ((timeoutSecs - now.tv_sec) * 1000000000); timedOut = (pthread_cond_timedwait (&fPthreadCondition, &mutex.fPthreadMutex, &now) == ETIMEDOUT); } mutex.fPrevHeldMutex = innermostMutex; gInnermostMutexHolder.SetInnermostMutex (&mutex); return !timedOut; } /*****************************************************************************/ void dng_condition::Signal () { int result; result = pthread_cond_signal (&fPthreadCondition); DNG_ASSERT (result == 0, "pthread_cond_signal failed."); if (result != 0) ThrowProgramError (); } /*****************************************************************************/ void dng_condition::Broadcast () { int result; result = pthread_cond_broadcast (&fPthreadCondition); DNG_ASSERT (result == 0, "pthread_cond_broadcast failed."); if (result != 0) ThrowProgramError (); } /*****************************************************************************/ #endif // qDNGThreadSafe /*****************************************************************************/