AutoLock.h 8.5 KB

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