NonBlockLock.h 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. #pragma warning (disable:4786)
  2. #ifndef NONBLOCKLOCKH
  3. #define NONBLOCKLOCKH
  4. #include <windows.h>
  5. /*
  6. NULLSOFT_LOCK_OUTPUT_STATUS turns on/off debugging output
  7. this can be VERY useful if you are trying to find a deadlock
  8. each time the guard is locked or unlocked, it outputs a list of
  9. any threads using the mutex, and their function stack
  10. */
  11. #define NULLSOFT_LOCK_OUTPUT_STATS
  12. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  13. #include <string> // we save each function name as a string
  14. #include <deque> // we make a list of the recursive function stack for each thread
  15. #include <map> // and map
  16. #include <iostream> // we output to std::cerr
  17. #include <windows.h>
  18. #endif
  19. /*****
  20. Description:
  21. This class uses scoping to wrap a critical section (lightweight in-process mutex)
  22. The constructor enters the mutex and the destructor leaves it. This allows it to
  23. take advantage of automatic scoping in C++, because C++ automatically calls the destructor
  24. when an object leaves scope.
  25. This is _especially_ useful when you have multiple return paths, since you don't have to
  26. repeat mutex-leaving code.
  27. To use:
  28. Make a LockGuard for a resource you want to protect. The guard is shared, so make it part
  29. of your class, or a global, or whatever. The LockGuard is essentially a "token", equivalent
  30. to your mutex handle or critical section handle.
  31. Make an AutoLock object on the stack to lock. It will unlock automatically when the object
  32. leaves scope.
  33. Note: You'll want to make an object on the stack - don't use a heap object (new/delete)
  34. unless you have weird requirements and know what you are doing.
  35. Example:
  36. class MyClass
  37. {
  38. LockGuard fileGuard;
  39. fstream file;
  40. void DumpSomeData() //
  41. {
  42. AutoLock lock(fileGuard);
  43. file << GetData();
  44. }
  45. void CALLBACK NewData() // potentially called by another thread
  46. {
  47. AutoLock lock(fileGuard)
  48. file << newData;
  49. }
  50. };
  51. Tip: You can use "false scoping" to tweak the mutex lifetime, for example:
  52. void DoStuff()
  53. {
  54. a = GetData();
  55. { // false scope
  56. AutoLock lock(dataGuard);
  57. DoCalculationsWith(a);
  58. } // mutex will release here
  59. SetData(a);
  60. }
  61. Tip: A common mistake is making a temporary object.
  62. i.e.
  63. CORRECT: AutoLock lock(fileGuard); // an AutoLock object called "lock" is put on the stack
  64. INCORRECT: AutoLock(fileGuard); // An unnamed temporary is created which will be destroyed IMMEDIATELY
  65. *******/
  66. namespace Nullsoft
  67. {
  68. namespace Utility
  69. {
  70. class NonBlockLock;
  71. /* the token which represents a resource to be locked */
  72. class NonBlockLockGuard
  73. {
  74. public:
  75. friend class NonBlockLock;
  76. inline NonBlockLockGuard(char *name = "Unnamed Guard")
  77. {
  78. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  79. lockName = name;
  80. InitializeCriticalSection(&cerr_cs);
  81. InitializeCriticalSection(&map_cs);
  82. #endif
  83. event=CreateEvent(NULL, FALSE, TRUE, NULL);
  84. ownerThread=-1;
  85. InitializeCriticalSection(&threads_cs);
  86. }
  87. inline ~NonBlockLockGuard()
  88. {
  89. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  90. DeleteCriticalSection(&cerr_cs);
  91. DeleteCriticalSection(&map_cs);
  92. #endif
  93. CloseHandle(event);
  94. DeleteCriticalSection(&threads_cs);
  95. }
  96. private:
  97. inline bool Lock()
  98. {
  99. HRESULT hr;
  100. EnterCriticalSection(&threads_cs);
  101. hr=WaitForSingleObject(event, 0);
  102. if (hr == WAIT_TIMEOUT && ownerThread==GetCurrentThreadId())
  103. {
  104. LeaveCriticalSection(&threads_cs);
  105. return false;
  106. }
  107. else if (hr == WAIT_OBJECT_0)
  108. {
  109. ownerThread=GetCurrentThreadId();
  110. LeaveCriticalSection(&threads_cs);
  111. return true;
  112. }
  113. LeaveCriticalSection(&threads_cs);
  114. do
  115. {
  116. EnterCriticalSection(&threads_cs);
  117. if (WaitForSingleObject(event, 3)==WAIT_OBJECT_0)
  118. {
  119. ownerThread=GetCurrentThreadId();
  120. LeaveCriticalSection(&threads_cs);
  121. break;
  122. }
  123. else
  124. {
  125. LeaveCriticalSection(&threads_cs);
  126. MSG msg;
  127. while(PeekMessage(&msg, NULL, 0, 0, 1))
  128. {
  129. //TranslateMessage(&msg);
  130. DispatchMessage(&msg);
  131. }
  132. Sleep(3);
  133. }
  134. } while(true);
  135. return true;
  136. }
  137. inline void Unlock()
  138. {
  139. //LeaveCriticalSection(&m_cs);
  140. EnterCriticalSection(&threads_cs);
  141. ownerThread=-1;
  142. SetEvent(event);
  143. LeaveCriticalSection(&threads_cs);
  144. }
  145. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  146. int ThreadCount()
  147. {
  148. EnterCriticalSection(&map_cs);
  149. int count = 0;
  150. for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
  151. {
  152. if (!itr->second.empty())
  153. count++;
  154. }
  155. LeaveCriticalSection(&map_cs);
  156. return count;
  157. }
  158. void Display()
  159. {
  160. EnterCriticalSection(&map_cs);
  161. EnterCriticalSection(&cerr_cs);
  162. if (ThreadCount() > 1 && owner)
  163. {
  164. std::cerr << "Guard: " << lockName << std::endl;
  165. for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
  166. {
  167. if (itr->second.empty())
  168. continue;
  169. std::cerr << " Thread ID: " << std::hex << itr->first << std::dec;
  170. if (owner == itr->first)
  171. std::cerr << " [holding the mutex] *****";
  172. else
  173. std::cerr << " [blocked]";
  174. std::cerr << std::endl;
  175. for (FunctionStack::iterator fitr = itr->second.begin(); fitr != itr->second.end(); fitr++)
  176. {
  177. std::cerr << " " << *fitr << "();" << std::endl;
  178. }
  179. }
  180. }
  181. LeaveCriticalSection(&cerr_cs);
  182. LeaveCriticalSection(&map_cs);
  183. }
  184. void In(DWORD thread, char *functionName)
  185. {
  186. EnterCriticalSection(&map_cs);
  187. threads[thread].push_back(functionName);
  188. LeaveCriticalSection(&map_cs);
  189. }
  190. void Out(DWORD thread)
  191. {
  192. EnterCriticalSection(&map_cs);
  193. threads[thread].pop_back();
  194. LeaveCriticalSection(&map_cs);
  195. }
  196. std::string lockName;
  197. CRITICAL_SECTION cerr_cs, map_cs;
  198. typedef std::deque<std::string> FunctionStack; // this typedef reduce ugly c++ <>::<>::<> overkill
  199. typedef std::map<DWORD, FunctionStack> ThreadMap;
  200. ThreadMap threads;
  201. DWORD owner;
  202. #endif
  203. private:
  204. //CRITICAL_SECTION m_cs;
  205. CRITICAL_SECTION threads_cs;
  206. HANDLE event;
  207. DWORD ownerThread;
  208. };
  209. /* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
  210. class NonBlockLock
  211. {
  212. public:
  213. /*
  214. @param functionName The function name which wants the mutex
  215. we pass it in as a char * even though it'll be converted to a std::string
  216. to reduce overhead when OUTPUT_STATS is off
  217. */
  218. inline NonBlockLock(NonBlockLockGuard &_guard, char *functionName = "function name not passed") : guard(&_guard), owner(false)
  219. {
  220. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  221. thisThread = GetCurrentThreadId();
  222. guard->In(thisThread, functionName);
  223. guard->Display();
  224. #endif
  225. owner=guard->Lock();
  226. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  227. guard->owner = thisThread;
  228. guard->Display();
  229. #endif
  230. }
  231. inline void ManualLock(char *functionName = "manual lock")
  232. {
  233. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  234. thisThread = GetCurrentThreadId();
  235. guard->In(thisThread,functionName);
  236. guard->Display();
  237. #endif
  238. owner=guard->Lock();
  239. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  240. guard->owner = thisThread;
  241. guard->Display();
  242. #endif
  243. }
  244. inline void ManualUnlock()
  245. {
  246. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  247. guard->Display();
  248. #endif
  249. if (owner)
  250. guard->Unlock();
  251. owner=false;
  252. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  253. InterlockedCompareExchange((LONG *)(void *)&guard->owner, 0, (LONG)thisThread);
  254. /* above line is functionally equivalent to:
  255. if (guard->owner == thisThread)
  256. guard->owner=0;
  257. */
  258. guard->Out(thisThread);
  259. guard->Display();
  260. #endif
  261. }
  262. inline ~NonBlockLock()
  263. {
  264. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  265. guard->Display();
  266. #endif
  267. if (owner)
  268. guard->Unlock();
  269. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  270. InterlockedCompareExchange((LONG *)(void *)&guard->owner, 0, (LONG)thisThread);
  271. /* above line is functionally equivalent to:
  272. if (guard->owner == thisThread)
  273. guard->owner=0;
  274. */
  275. guard->Out(thisThread);
  276. guard->Display();
  277. #endif
  278. }
  279. NonBlockLockGuard *guard;
  280. bool owner;
  281. #ifdef NULLSOFT_LOCK_OUTPUT_STATS
  282. DWORD thisThread;
  283. #endif
  284. };
  285. }
  286. }
  287. #endif