Vault
4.1
|
00001 /* 00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved. 00003 This file is part of the Code Vault version 4.1 00004 http://www.bombaydigital.com/ 00005 License: MIT. See LICENSE.md in the Vault top level directory. 00006 */ 00007 00010 #include "vmutex.h" 00011 00012 #include "vexception.h" 00013 #include "vthread.h" 00014 #include "vlogger.h" 00015 00016 VDuration VMutex::gVMutexLockDelayLoggingThreshold(100 * VDuration::MILLISECOND()); 00017 int VMutex::gVMutexLockDelayLoggingLevel(VLoggerLevel::DEBUG); 00018 00019 VMutex::VMutex(const VString& name, bool suppressLogging) 00020 : mMutex() 00021 , mName(name) 00022 , mSuppressLogging(suppressLogging) 00023 , mLastLockThread((VThreadID_Type) - 1) 00024 , mLastLockerName() 00025 , mLastLockTime(VInstant::NEVER_OCCURRED()) 00026 , mIsLocked(false) 00027 { 00028 00029 if (! VMutex::mutexInit(&mMutex)) 00030 throw VStackTraceException(VSTRING_FORMAT("VMutex::VMutex unable to initialize mutex '%s'.", name.chars())); 00031 } 00032 00033 VMutex::~VMutex() { 00034 VMutex::mutexDestroy(&mMutex); 00035 } 00036 00037 void VMutex::setName(const VString& name) { 00038 mName = name; 00039 } 00040 00041 VMutex_Type* VMutex::getMutex() { 00042 return &mMutex; 00043 } 00044 00045 bool VMutex::isLockedByCurrentThread() const { 00046 return mIsLocked && (mLastLockThread == VThread::threadSelf()); 00047 } 00048 00049 void VMutex::_lock(const VString& lockerName) { 00050 #ifdef VAULT_MUTEX_LOCK_DELAY_CHECK 00051 VInstant start; 00052 #endif 00053 if (VMutex::mutexLock(&mMutex)) { 00054 mLastLockTime.setNow(); 00055 00056 #ifdef VAULT_MUTEX_LOCK_DELAY_CHECK 00057 if ((gVMutexLockDelayLoggingThreshold >= VDuration::ZERO()) && ! mSuppressLogging) { 00058 VInstant end; 00059 VDuration waitTime = end - start; 00060 00061 if (waitTime >= gVMutexLockDelayLoggingThreshold) { 00062 VLOGGER_LEVEL(gVMutexLockDelayLoggingLevel, VSTRING_FORMAT("Delay: '%s' was blocked " VSTRING_FORMATTER_S64 "ms on mutex '%s' released by '%s'.", 00063 lockerName.chars(), waitTime.getDurationMilliseconds(), mName.chars(), mLastLockerName.chars())); 00064 } 00065 } 00066 #endif 00067 00068 // Note: These properties are only valid with the understanding that they are not set atomically during lock/unlock. 00069 // The only guarantee is that they end up set to their new values after the lock is acquired above, and before we return. 00070 // They may only be used for mutex diagnostics (e.g. isLockedByCurrentThread() and lock delay reporting), not for concurrency control. 00071 mLastLockThread = VThread::threadSelf(); 00072 mLastLockerName = lockerName; 00073 mIsLocked = true; 00074 } else { 00075 if (mName.isEmpty()) { 00076 throw VStackTraceException("VMutex::lock unable to lock mutex."); 00077 } else { 00078 throw VStackTraceException(VSTRING_FORMAT("VMutex::lock unable to lock mutex '%s'.", mName.chars())); 00079 } 00080 } 00081 } 00082 00083 void VMutex::_unlock() { 00084 #ifdef VAULT_MUTEX_LOCK_DELAY_CHECK 00085 if ((gVMutexLockDelayLoggingThreshold >= VDuration::ZERO()) && ! mSuppressLogging) { 00086 VInstant now; 00087 VDuration delay = now - mLastLockTime; 00088 if (delay >= gVMutexLockDelayLoggingThreshold) { 00089 VLOGGER_LEVEL(gVMutexLockDelayLoggingLevel, VSTRING_FORMAT("Delay: '%s' is unlocking mutex '%s' after holding it for " VSTRING_FORMATTER_S64 "ms.", 00090 mLastLockerName.chars(), mName.chars(), delay.getDurationMilliseconds())); 00091 } 00092 } 00093 #endif 00094 00095 mIsLocked = false; // Note: Must set false *before* unlocking, otherwise we may set it false after another thread jumps in, locks, sets it true, confusing isLockedByCurrentThread(). Part of non-atomicity warning above. 00096 if (! VMutex::mutexUnlock(&mMutex)) { 00097 mIsLocked = true; // Restore value since we failed to unlock. 00098 if (mName.isEmpty()) { 00099 throw VStackTraceException("VMutex::unlock unable to unlock mutex."); 00100 } else { 00101 throw VStackTraceException(VSTRING_FORMAT("VMutex::unlock unable to unlock mutex '%s'.", mName.chars())); 00102 } 00103 } 00104 } 00105