diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/replicant/nu/AutoLock.h | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/replicant/nu/AutoLock.h')
-rw-r--r-- | Src/replicant/nu/AutoLock.h | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/Src/replicant/nu/AutoLock.h b/Src/replicant/nu/AutoLock.h new file mode 100644 index 00000000..02370cfc --- /dev/null +++ b/Src/replicant/nu/AutoLock.h @@ -0,0 +1,377 @@ +#ifndef AUTOLOCKH +#define AUTOLOCKH + +#ifdef _WIN32 +#pragma warning (disable:4786) +#include <windows.h> +#elif defined(__linux__) || defined(__APPLE__) +#include <pthread.h> +#else +#error port me!! +#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 <string> // we save each function name as a string +#include <deque> // we make a list of the recursive function stack for each thread +#include <map> // and map +#include <iostream> // we output to std::cerr +#include <windows.h> + + +/***** +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(const char *name = "Unnamed Guard") + { + lockName = name; + 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) + { + std::cerr << "Guard: " << lockName << std::endl; + for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++) + { + if (itr->second.empty()) + continue; + + std::cerr << " Thread ID: " << std::hex << itr->first << std::dec; + if (owner == itr->first) + std::cerr << " [holding the mutex] *****"; + else + std::cerr << " [blocked]"; + std::cerr << std::endl; + for (FunctionStack::iterator fitr = itr->second.begin(); fitr != itr->second.end(); fitr++) + { + std::cerr << " " << *fitr << "();" << std::endl; + } + } + } + LeaveCriticalSection(&cerr_cs); + LeaveCriticalSection(&map_cs); + } + + void In(DWORD thread, const 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<std::string> FunctionStack; // this typedef reduce ugly c++ <>::<>::<> overkill + typedef std::map<DWORD, FunctionStack> 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, const 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 nu +{ + /* the token which represents a resource to be locked */ + class LockGuard + { + public: + inline LockGuard(const char *guardName = "") + { + #ifdef _WIN32 + InitializeCriticalSection(&m_cs); +#elif defined(__linux__) + pthread_mutexattr_t mtxattr; + pthread_mutexattr_init(&mtxattr); + pthread_mutexattr_settype(&mtxattr, PTHREAD_MUTEX_RECURSIVE_NP ); + pthread_mutex_init(&mtx, &mtxattr); + pthread_mutexattr_destroy(&mtxattr); +#elif defined(__APPLE__) + pthread_mutexattr_t mtxattr; + pthread_mutexattr_init(&mtxattr); + pthread_mutexattr_settype(&mtxattr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mtx, &mtxattr); + pthread_mutexattr_destroy(&mtxattr); + #else + #error port me + #endif + } + inline ~LockGuard() + { + #ifdef _WIN32 + DeleteCriticalSection(&m_cs); + #elif defined(__linux__) || defined(__APPLE__) + pthread_mutex_destroy(&mtx); + #else + #error port me! + #endif + } + inline void Lock() + { + #ifdef _WIN32 + EnterCriticalSection(&m_cs); + #elif defined(__linux__) || defined(__APPLE__) + pthread_mutex_lock(&mtx); + #else + #error por tme! + #endif + } + + inline void Unlock() + { + #ifdef _WIN32 + LeaveCriticalSection(&m_cs); + #elif defined(__linux__) || defined(__APPLE__) + pthread_mutex_unlock(&mtx); + #else + #error port me! + #endif + } + private: + #ifdef _WIN32 + CRITICAL_SECTION m_cs; + #elif defined(__linux__) || defined(__APPLE__) + pthread_mutex_t mtx; + #else + #error port me! + #endif + }; + + /* 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 LockGuard_t> + 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 |