#pragma warning (disable:4786) #ifndef AUTOLOCKH #define AUTOLOCKH #ifdef _WIN32 #include #else #include #endif /* NULLSOFT_LOCK_OUTPUT_STATUS turns on/off debugging output this can be VERY useful if you are trying to find a deadlock each time the guard is locked or unlocked, it outputs a list of any threads using the mutex, and their function stack */ //#define NULLSOFT_LOCK_OUTPUT_STATS #ifdef NULLSOFT_LOCK_OUTPUT_STATS #include // we save each function name as a string #include // we make a list of the recursive function stack for each thread #include // and map #include // we output to std::cerr #include /***** Description: This class uses scoping to wrap a critical section (lightweight in-process mutex) The constructor enters the mutex and the destructor leaves it. This allows it to take advantage of automatic scoping in C++, because C++ automatically calls the destructor when an object leaves scope. This is _especially_ useful when you have multiple return paths, since you don't have to repeat mutex-leaving code. To use: Make a LockGuard for a resource you want to protect. The guard is shared, so make it part of your class, or a global, or whatever. The LockGuard is essentially a "token", equivalent to your mutex handle or critical section handle. Make an AutoLock object on the stack to lock. It will unlock automatically when the object leaves scope. Note: You'll want to make an object on the stack - don't use a heap object (new/delete) unless you have weird requirements and know what you are doing. Example: class MyClass { LockGuard fileGuard; fstream file; void DumpSomeData() // { AutoLock lock(fileGuard); file << GetData(); } void CALLBACK NewData() // potentially called by another thread { AutoLock lock(fileGuard) file << newData; } }; Tip: You can use "false scoping" to tweak the mutex lifetime, for example: void DoStuff() { a = GetData(); { // false scope AutoLock lock(dataGuard); DoCalculationsWith(a); } // mutex will release here SetData(a); } Tip: A common mistake is making a temporary object. i.e. CORRECT: AutoLock lock(fileGuard); // an AutoLock object called "lock" is put on the stack INCORRECT: AutoLock(fileGuard); // An unnamed temporary is created which will be destroyed IMMEDIATELY *******/ #define MANUALLOCKNAME(x) x #define LOCKNAME(x) ,x #define GUARDNAME(x) (x) namespace Nullsoft { namespace Utility { /* the token which represents a resource to be locked */ class LockGuard { public: inline LockGuard(char *name = "Unnamed Guard") : lockName(name), owner(0) { InitializeCriticalSection(&cerr_cs); InitializeCriticalSection(&map_cs); InitializeCriticalSection(&m_cs); } inline LockGuard(DWORD spin_count,char *name = "Unnamed Guard") : lockName(name), owner(0) { InitializeCriticalSection(&cerr_cs); InitializeCriticalSection(&map_cs); InitializeCriticalSection(&m_cs); } inline ~LockGuard() { DeleteCriticalSection(&cerr_cs); DeleteCriticalSection(&map_cs); DeleteCriticalSection(&m_cs); } inline void Lock() { EnterCriticalSection(&m_cs); } inline void Unlock() { LeaveCriticalSection(&m_cs); } int ThreadCount() { EnterCriticalSection(&map_cs); int count = 0; for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++) { if (!itr->second.empty()) count++; } LeaveCriticalSection(&map_cs); return count; } void Display() { EnterCriticalSection(&map_cs); EnterCriticalSection(&cerr_cs); if (ThreadCount() > 1 && owner) { wchar_t disp[256]; wsprintfW(disp, L"Guard: %S\r\n", lockName.c_str()); OutputDebugStringW(disp); for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++) { if (itr->second.empty()) continue; wsprintfW(disp, L" Thread ID: %x", itr->first); if (owner == itr->first) wcscat(disp, L" [holding the mutex] *****\r\n"); else wcscat(disp, L" [blocked]\r\n"); OutputDebugStringW(disp); for (FunctionStack::iterator fitr = itr->second.begin(); fitr != itr->second.end(); fitr++) { wsprintfW(disp, L" %S();\r\n", fitr->c_str()); OutputDebugStringW(disp); } } } LeaveCriticalSection(&cerr_cs); LeaveCriticalSection(&map_cs); } void In(DWORD thread, char *functionName) { EnterCriticalSection(&map_cs); threads[thread].push_back(functionName); LeaveCriticalSection(&map_cs); } void Out(DWORD thread) { EnterCriticalSection(&map_cs); threads[thread].pop_back(); LeaveCriticalSection(&map_cs); } std::string lockName; CRITICAL_SECTION cerr_cs, map_cs; typedef std::deque FunctionStack; // this typedef reduce ugly c++ <>::<>::<> overkill typedef std::map ThreadMap; ThreadMap threads; DWORD owner; private: CRITICAL_SECTION m_cs; }; /* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */ class AutoLock { public: /* @param functionName The function name which wants the mutex we pass it in as a char * even though it'll be converted to a std::string to reduce overhead when OUTPUT_STATS is off */ inline AutoLock(LockGuard &_guard, char *functionName = "function name not passed") : guard(&_guard) { ManualLock(functionName); } inline void ManualLock(char *functionName = "manual lock") { thisThread = GetCurrentThreadId(); guard->In(thisThread, functionName); guard->Display(); guard->Lock(); guard->owner = thisThread; guard->Display(); } inline void ManualUnlock() { guard->Display(); guard->Unlock(); InterlockedCompareExchange((LONG volatile *)&guard->owner, 0, (LONG)thisThread); /* above line is functionally equivalent to: if (guard->owner == thisThread) guard->owner=0; */ guard->Out(thisThread); guard->Display(); } inline ~AutoLock() { ManualUnlock(); } LockGuard *guard; DWORD thisThread; }; } } #else #define MANUALLOCKNAME(x) #define LOCKNAME(x) #define GUARDNAME(x) namespace Nullsoft { namespace Utility { /* the token which represents a resource to be locked */ class LockGuard { public: inline LockGuard(char *guardName = "") { #ifdef _WIN32 InitializeCriticalSection(&m_cs); #else MPCreateCriticalRegion(&cr); #endif } #if _WIN32_WINNT >= 0x403 inline LockGuard(DWORD spin_count, char *guardName = "") { if (spin_count) InitializeCriticalSectionAndSpinCount(&m_cs, spin_count); else InitializeCriticalSection(&m_cs); } #endif inline ~LockGuard() { #ifdef _WIN32 DeleteCriticalSection(&m_cs); #else MPDeleteCriticalRegion(cr); #endif } inline void Lock() { #ifdef _WIN32 EnterCriticalSection(&m_cs); #else MPEnterCriticalRegion(cr, kDurationForever); #endif } inline void Unlock() { #ifdef _WIN32 LeaveCriticalSection(&m_cs); #else MPExitCriticalRegion(cr); #endif } private: #ifdef _WIN32 CRITICAL_SECTION m_cs; #else MPCriticalRegionID cr; #endif LockGuard(const LockGuard ©) { } // make copy constructor private so it can't be used LockGuard &operator =(const LockGuard ©) {} // same with operator= }; /* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */ class AutoLock { public: inline AutoLock(LockGuard &_guard) : guard(&_guard) { guard->Lock(); } inline AutoLock(LockGuard *_guard) : guard(_guard) { guard->Lock(); } inline void ManualLock() { guard->Lock(); } inline void ManualUnlock() { guard->Unlock(); } inline ~AutoLock() { guard->Unlock(); } LockGuard *guard; }; // will lock anything that implements Lock() and Unlock() template class AutoLockT { public: inline AutoLockT(LockGuard_t &_guard) : guard(&_guard) { guard->Lock(); } inline AutoLockT(LockGuard_t *_guard) : guard(_guard) { guard->Lock(); } inline void ManualLock() { guard->Lock(); } inline void ManualUnlock() { guard->Unlock(); } inline ~AutoLockT() { guard->Unlock(); } LockGuard_t *guard; }; } } #endif #endif