123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- #pragma warning (disable:4786)
- #ifndef NONBLOCKLOCKH
- #define NONBLOCKLOCKH
- #include <windows.h>
- /*
- 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>
- #endif
- /*****
- 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
- *******/
- namespace Nullsoft
- {
- namespace Utility
- {
- class NonBlockLock;
- /* the token which represents a resource to be locked */
- class NonBlockLockGuard
- {
- public:
- friend class NonBlockLock;
- inline NonBlockLockGuard(char *name = "Unnamed Guard")
- {
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- lockName = name;
- InitializeCriticalSection(&cerr_cs);
- InitializeCriticalSection(&map_cs);
- #endif
- event=CreateEvent(NULL, FALSE, TRUE, NULL);
- ownerThread=-1;
- InitializeCriticalSection(&threads_cs);
- }
- inline ~NonBlockLockGuard()
- {
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- DeleteCriticalSection(&cerr_cs);
- DeleteCriticalSection(&map_cs);
- #endif
- CloseHandle(event);
- DeleteCriticalSection(&threads_cs);
- }
- private:
- inline bool Lock()
- {
- HRESULT hr;
- EnterCriticalSection(&threads_cs);
- hr=WaitForSingleObject(event, 0);
- if (hr == WAIT_TIMEOUT && ownerThread==GetCurrentThreadId())
- {
- LeaveCriticalSection(&threads_cs);
- return false;
- }
- else if (hr == WAIT_OBJECT_0)
- {
- ownerThread=GetCurrentThreadId();
- LeaveCriticalSection(&threads_cs);
- return true;
- }
- LeaveCriticalSection(&threads_cs);
- do
- {
- EnterCriticalSection(&threads_cs);
- if (WaitForSingleObject(event, 3)==WAIT_OBJECT_0)
- {
- ownerThread=GetCurrentThreadId();
- LeaveCriticalSection(&threads_cs);
- break;
- }
- else
- {
- LeaveCriticalSection(&threads_cs);
- MSG msg;
- while(PeekMessage(&msg, NULL, 0, 0, 1))
- {
- //TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- Sleep(3);
- }
- } while(true);
- return true;
- }
- inline void Unlock()
- {
- //LeaveCriticalSection(&m_cs);
- EnterCriticalSection(&threads_cs);
- ownerThread=-1;
- SetEvent(event);
- LeaveCriticalSection(&threads_cs);
- }
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- 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, 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;
- #endif
- private:
- //CRITICAL_SECTION m_cs;
- CRITICAL_SECTION threads_cs;
- HANDLE event;
- DWORD ownerThread;
- };
- /* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
- class NonBlockLock
- {
- 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 NonBlockLock(NonBlockLockGuard &_guard, char *functionName = "function name not passed") : guard(&_guard), owner(false)
- {
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- thisThread = GetCurrentThreadId();
- guard->In(thisThread, functionName);
- guard->Display();
- #endif
- owner=guard->Lock();
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- guard->owner = thisThread;
- guard->Display();
- #endif
- }
- inline void ManualLock(char *functionName = "manual lock")
- {
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- thisThread = GetCurrentThreadId();
- guard->In(thisThread,functionName);
- guard->Display();
- #endif
- owner=guard->Lock();
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- guard->owner = thisThread;
- guard->Display();
- #endif
- }
- inline void ManualUnlock()
- {
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- guard->Display();
- #endif
- if (owner)
- guard->Unlock();
- owner=false;
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- InterlockedCompareExchange((LONG *)(void *)&guard->owner, 0, (LONG)thisThread);
- /* above line is functionally equivalent to:
- if (guard->owner == thisThread)
- guard->owner=0;
- */
- guard->Out(thisThread);
- guard->Display();
- #endif
- }
- inline ~NonBlockLock()
- {
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- guard->Display();
- #endif
- if (owner)
- guard->Unlock();
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- InterlockedCompareExchange((LONG *)(void *)&guard->owner, 0, (LONG)thisThread);
- /* above line is functionally equivalent to:
- if (guard->owner == thisThread)
- guard->owner=0;
- */
- guard->Out(thisThread);
- guard->Display();
- #endif
- }
- NonBlockLockGuard *guard;
- bool owner;
- #ifdef NULLSOFT_LOCK_OUTPUT_STATS
- DWORD thisThread;
- #endif
- };
- }
- }
- #endif
|