AutoLock.h 8.7 KB

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