Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

400 lines
11 KiB

  1. //*****************************************************************************
  2. // locks.h
  3. //
  4. // This class provides a number of locking primitives for multi-threaded
  5. // programming. The main class of interest are:
  6. // CCritLock Critical section based lock wrapper class.
  7. // CExclLock A Spin lock class for classic test & set behavior.
  8. // CSingleLock A spin lock with no nesting capabilities.
  9. // CAutoLock A helper class to lock/unlock in ctor/dtor.
  10. //
  11. // CMReadSWrite A highly efficient lock for multiple readers and
  12. // single writer behavior.
  13. // CAutoReadLock A helper for read locking in ctor/dtor.
  14. // CAutoWriteLock A helper for write locking in ctor/dtor.
  15. //
  16. // Copyright (c) 1996, Microsoft Corp. All rights reserved.
  17. //*****************************************************************************
  18. #ifndef __LOCKS_H__
  19. #define __LOCKS_H__
  20. //*****************************************************************************
  21. // This lock implements a spin lock that does not support nesting. It is very
  22. // lean because of this, but locks cannot be nested.
  23. //*****************************************************************************
  24. class CSingleLock
  25. {
  26. long volatile m_iLock; // Test and set spin value.
  27. public:
  28. inline CSingleLock() :
  29. m_iLock(0)
  30. { }
  31. inline ~CSingleLock()
  32. {
  33. m_iLock = 0;
  34. }
  35. //*****************************************************************************
  36. // This version spins forever until it wins. Nested calls to Lock from the
  37. // same thread are not supported.
  38. //*****************************************************************************
  39. inline void Lock()
  40. {
  41. // Spin until we win.
  42. while (InterlockedExchange((long*)&m_iLock, 1L) == 1L)
  43. ;
  44. }
  45. //*****************************************************************************
  46. // This version spins until it wins or times out. Nested calls to Lock from
  47. // the same thread are supported.
  48. //*****************************************************************************
  49. HRESULT Lock( // S_OK, or E_FAIL.
  50. DWORD dwTimeout) // Millisecond timeout value, 0 is forever.
  51. {
  52. DWORD dwTime = 0;
  53. // Keep spinning until we get the lock.
  54. while (InterlockedExchange((long*)&m_iLock, 1L) == 1L)
  55. {
  56. // Wait for 1/10 a second.
  57. Sleep(100);
  58. // See if we have gone over the timeout value.
  59. if (dwTimeout)
  60. {
  61. if ((dwTime += 100) >= dwTimeout)
  62. return (E_FAIL);
  63. }
  64. }
  65. return (S_OK);
  66. }
  67. //*****************************************************************************
  68. // Assigning to 0 is thread safe and yields much faster performance than
  69. // an Interlocked* operation.
  70. //*****************************************************************************
  71. inline void Unlock()
  72. {
  73. m_iLock = 0;
  74. }
  75. };
  76. //*****************************************************************************
  77. // This lock class is based on NT's critical sections and has all of their
  78. // semantics.
  79. //*****************************************************************************
  80. class CCritLock
  81. {
  82. private:
  83. CRITICAL_SECTION m_sCrit; // The critical section to block on.
  84. #ifdef _DEBUG
  85. BOOL m_bInit; // Track init status.
  86. int m_iLocks; // Count of locks.
  87. #endif
  88. public:
  89. inline CCritLock()
  90. {
  91. #ifdef _DEBUG
  92. m_bInit = TRUE;
  93. m_iLocks = 0;
  94. #endif
  95. InitializeCriticalSection(&m_sCrit);
  96. }
  97. inline ~CCritLock()
  98. {
  99. _ASSERTE(m_bInit);
  100. _ASSERTE(m_iLocks == 0);
  101. DeleteCriticalSection(&m_sCrit);
  102. }
  103. inline void Lock()
  104. {
  105. _ASSERTE(m_bInit);
  106. EnterCriticalSection(&m_sCrit);
  107. _ASSERTE(++m_iLocks > 0);
  108. }
  109. inline void Unlock()
  110. {
  111. _ASSERTE(m_bInit);
  112. _ASSERTE(--m_iLocks >= 0);
  113. LeaveCriticalSection(&m_sCrit);
  114. }
  115. #ifdef _DEBUG
  116. inline int GetLockCnt()
  117. { return (m_iLocks); }
  118. inline BOOL IsLocked()
  119. { return (m_iLocks != 0); }
  120. #endif
  121. };
  122. //*****************************************************************************
  123. // Provides a mututal exclusion lock for a resource through a spin lock. This
  124. // type of lock does not keep a queue, so thread starvation is theoretically
  125. // possible. In addition, thread priority could cause a potential dead lock if
  126. // a low priority thread got the lock but didn't get enough time to eventually
  127. // free it.
  128. // NOTE: There is a bug in the Pentium cache that InterlockedExchange will
  129. // force a cache flush of the value. For this reason, doing an assignment
  130. // to free the lock is much, much faster than using an Interlocked instruction.
  131. //*****************************************************************************
  132. class CExclLock
  133. {
  134. long volatile m_iLock; // Test and set spin value.
  135. long m_iNest; // Nesting count.
  136. DWORD m_iThreadId; // The thread that owns the lock.
  137. public:
  138. inline CExclLock() :
  139. m_iLock(0),
  140. m_iNest(0),
  141. m_iThreadId(0)
  142. { }
  143. inline ~CExclLock()
  144. {
  145. m_iNest = 0;
  146. m_iThreadId = 0;
  147. m_iLock = 0;
  148. }
  149. //*****************************************************************************
  150. // This version spins forever until it wins. Nested calls to Lock from the
  151. // same thread are supported.
  152. //*****************************************************************************
  153. inline void Lock()
  154. {
  155. DWORD iThread; // Local thread ID.
  156. // Allow nested calls to lock in the same thread.
  157. if ((iThread = GetCurrentThreadId()) == m_iThreadId && m_iLock)
  158. {
  159. ++m_iNest;
  160. return;
  161. }
  162. // Spin until we win.
  163. while (InterlockedExchange((long*)&m_iLock, 1L) == 1L)
  164. ;
  165. // Store our thread ID and nesting count now that we've won.
  166. m_iThreadId = iThread;
  167. m_iNest = 1;
  168. }
  169. //*****************************************************************************
  170. // This version spins until it wins or times out. Nested calls to Lock from
  171. // the same thread are supported.
  172. //*****************************************************************************
  173. HRESULT Lock( // S_OK, or E_FAIL.
  174. DWORD dwTimeout) // Millisecond timeout value, 0 is forever.
  175. {
  176. DWORD dwTime = 0;
  177. DWORD iThread; // Local thread ID.
  178. // Allow nested calls to lock in the same thread.
  179. if (m_iLock && (iThread = GetCurrentThreadId()) == m_iThreadId)
  180. {
  181. ++m_iNest;
  182. return (S_OK);
  183. }
  184. // Keep spinning until we get the lock.
  185. while (InterlockedExchange((long*)&m_iLock, 1L) == 1L)
  186. {
  187. // Wait for 1/10 a second.
  188. Sleep(100);
  189. // See if we have gone over the timeout value.
  190. if (dwTimeout)
  191. {
  192. if ((dwTime += 100) >= dwTimeout)
  193. return (E_FAIL);
  194. }
  195. }
  196. // Store our thread ID and nesting count now that we've won.
  197. m_iThreadId = iThread;
  198. m_iNest = 1;
  199. return (S_OK);
  200. }
  201. //*****************************************************************************
  202. // Assigning to 0 is thread safe and yields much faster performance than
  203. // an Interlocked* operation.
  204. //*****************************************************************************
  205. inline void Unlock()
  206. {
  207. _ASSERTE(m_iThreadId == GetCurrentThreadId() && m_iNest > 0);
  208. // Unlock outer nesting level.
  209. if (--m_iNest == 0)
  210. {
  211. m_iThreadId = 0;
  212. m_iLock = 0;
  213. }
  214. }
  215. #ifdef _DEBUG
  216. inline BOOL IsLocked()
  217. { return (m_iLock); }
  218. #endif
  219. };
  220. //*****************************************************************************
  221. // This helper class automatically locks the given lock object in the ctor and
  222. // frees it in the dtor. This makes your code slightly cleaner by not
  223. // requiring an unlock in all failure conditions.
  224. //*****************************************************************************
  225. class CAutoLock
  226. {
  227. CExclLock *m_psLock; // The lock object to free up.
  228. CCritLock *m_psCrit; // Crit lock.
  229. CSingleLock *m_psSingle; // Single non-nested lock.
  230. int m_iNest; // Nesting count for the item.
  231. public:
  232. //*****************************************************************************
  233. // Use this ctor with the assignment operators to do deffered locking.
  234. //*****************************************************************************
  235. CAutoLock() :
  236. m_psLock(NULL),
  237. m_psCrit(NULL),
  238. m_psSingle(NULL),
  239. m_iNest(0)
  240. {
  241. }
  242. //*****************************************************************************
  243. // This version handles a spin lock.
  244. //*****************************************************************************
  245. CAutoLock(CExclLock *psLock) :
  246. m_psLock(psLock),
  247. m_psCrit(NULL),
  248. m_psSingle(NULL),
  249. m_iNest(1)
  250. {
  251. _ASSERTE(psLock != NULL);
  252. psLock->Lock();
  253. }
  254. //*****************************************************************************
  255. // This version handles a critical section lock.
  256. //*****************************************************************************
  257. CAutoLock(CCritLock *psLock) :
  258. m_psLock(NULL),
  259. m_psCrit(psLock),
  260. m_psSingle(NULL),
  261. m_iNest(1)
  262. {
  263. _ASSERTE(psLock != NULL);
  264. psLock->Lock();
  265. }
  266. //*****************************************************************************
  267. // This version handles a critical section lock.
  268. //*****************************************************************************
  269. CAutoLock(CSingleLock *psLock) :
  270. m_psLock(NULL),
  271. m_psCrit(NULL),
  272. m_psSingle(psLock),
  273. m_iNest(1)
  274. {
  275. _ASSERTE(psLock != NULL);
  276. psLock->Lock();
  277. }
  278. //*****************************************************************************
  279. // Free the lock we actually have.
  280. //*****************************************************************************
  281. ~CAutoLock()
  282. {
  283. // If we actually took a lock, unlock it.
  284. if (m_iNest != 0)
  285. {
  286. if (m_psLock)
  287. {
  288. while (m_iNest--)
  289. m_psLock->Unlock();
  290. }
  291. else if (m_psCrit)
  292. {
  293. while (m_iNest--)
  294. m_psCrit->Unlock();
  295. }
  296. else if (m_psSingle)
  297. {
  298. while (m_iNest--)
  299. m_psSingle->Unlock();
  300. }
  301. }
  302. }
  303. //*****************************************************************************
  304. // Lock after ctor runs with NULL.
  305. //*****************************************************************************
  306. void Lock(
  307. CSingleLock *psLock)
  308. {
  309. m_psSingle = psLock;
  310. psLock->Lock();
  311. m_iNest = 1;
  312. }
  313. //*****************************************************************************
  314. // Assignment causes a lock to occur. dtor will free the lock. Nested
  315. // assignments are allowed.
  316. //*****************************************************************************
  317. CAutoLock & operator=( // Reference to this class.
  318. CExclLock *psLock) // The lock.
  319. {
  320. _ASSERTE(m_psCrit == NULL && m_psSingle == NULL);
  321. ++m_iNest;
  322. m_psLock = psLock;
  323. psLock->Lock();
  324. return (*this);
  325. }
  326. //*****************************************************************************
  327. // Assignment causes a lock to occur. dtor will free the lock. Nested
  328. // assignments are allowed.
  329. //*****************************************************************************
  330. CAutoLock & operator=( // Reference to this class.
  331. CCritLock *psLock) // The lock.
  332. {
  333. _ASSERTE(m_psSingle == NULL && m_psLock == NULL);
  334. ++m_iNest;
  335. m_psCrit = psLock;
  336. psLock->Lock();
  337. return (*this);
  338. }
  339. //*****************************************************************************
  340. // Assignment causes a lock to occur. dtor will free the lock. Nested
  341. // assignments are allowed.
  342. //*****************************************************************************
  343. CAutoLock & operator=( // Reference to this class.
  344. CSingleLock *psLock) // The lock.
  345. {
  346. _ASSERTE(m_psCrit == NULL && m_psLock == NULL);
  347. ++m_iNest;
  348. m_psSingle = psLock;
  349. psLock->Lock();
  350. return (*this);
  351. }
  352. };
  353. #endif // __LOCKS_H__