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.

362 lines
9.5 KiB

  1. /*++
  2. Copyright (C) 1996-2001 Microsoft Corporation
  3. Module Name:
  4. LOCK.CPP
  5. Abstract:
  6. Implements the generic class for obtaining read and write locks to some
  7. resource.
  8. See lock.h for all documentation.
  9. Classes defined:
  10. CLock
  11. History:
  12. a-levn 5-Sept-96 Created.
  13. 3/10/97 a-levn Fully documented
  14. --*/
  15. #include "precomp.h"
  16. #include <stdio.h>
  17. #include "lock.h"
  18. #include <stdio.h>
  19. // debugging.
  20. #define PRINTF
  21. //******************************************************************************
  22. //
  23. // See lock.h for documentation
  24. //
  25. //******************************************************************************
  26. CLock::CLock() : m_nReading(0), m_nWriting(0), m_nWaitingToRead(0),
  27. m_nWaitingToWrite(0)
  28. {
  29. // Initialize the critical sections
  30. // ================================
  31. InitializeCriticalSection(&m_csAll);
  32. InitializeCriticalSection(&m_csEntering);
  33. // Create unnamed events for reading and writing
  34. // =============================================
  35. m_hCanRead = CreateEvent(NULL, TRUE, TRUE, NULL);
  36. m_hCanWrite = CreateEvent(NULL, TRUE, TRUE, NULL);
  37. }
  38. //******************************************************************************
  39. //
  40. // See lock.h for documentation
  41. //
  42. //******************************************************************************
  43. CLock::~CLock()
  44. {
  45. CloseHandle(m_hCanWrite);
  46. CloseHandle(m_hCanRead);
  47. DeleteCriticalSection(&m_csAll);
  48. DeleteCriticalSection(&m_csEntering);
  49. }
  50. BOOL CLock::IsHoldingReadLock()
  51. {
  52. // Check if this thread already owns this lock
  53. // ===========================================
  54. EnterCriticalSection(&m_csAll);
  55. DWORD_PTR dwThreadId = GetCurrentThreadId();
  56. for(int i = 0; i < m_adwReaders.Size(); i++)
  57. {
  58. if(dwThreadId == (DWORD_PTR)m_adwReaders[i])
  59. {
  60. LeaveCriticalSection(&m_csAll);
  61. return TRUE;
  62. }
  63. }
  64. LeaveCriticalSection(&m_csAll);
  65. return FALSE;
  66. }
  67. //******************************************************************************
  68. //
  69. // See lock.h for documentation
  70. //
  71. //******************************************************************************
  72. int CLock::ReadLock(DWORD dwTimeout)
  73. {
  74. PRINTF("%d wants to read\n", GetCurrentThreadId());
  75. // Check if this thread already owns this lock
  76. // ===========================================
  77. EnterCriticalSection(&m_csAll);
  78. DWORD_PTR dwThreadId = GetCurrentThreadId();
  79. for(int i = 0; i < m_adwReaders.Size(); i++)
  80. {
  81. if(dwThreadId == (DWORD_PTR)m_adwReaders[i])
  82. {
  83. // We already have it --- add it to the list and return
  84. // ====================================================
  85. m_adwReaders.Add((void*)dwThreadId);
  86. m_nReading++;
  87. LeaveCriticalSection(&m_csAll);
  88. return NoError;
  89. }
  90. }
  91. // Don't have it already
  92. // =====================
  93. LeaveCriticalSection(&m_csAll);
  94. // Get in line for getting any kind of lock (those unlocking don't go into
  95. // this line)
  96. // =======================================================================
  97. EnterCriticalSection(&m_csEntering);
  98. // We are the only ones allowed to get any kind of lock now. Wait for the
  99. // event indicating that reading is enabled to become signaled
  100. // ======================================================================
  101. PRINTF("%d next to enter\n", GetCurrentThreadId());
  102. if(m_nWriting != 0)
  103. {
  104. int nRes = WaitFor(m_hCanRead, dwTimeout);
  105. if(nRes != NoError)
  106. {
  107. LeaveCriticalSection(&m_csEntering);
  108. return nRes;
  109. }
  110. }
  111. // Enter inner critical section (unlockers use it too), increment the
  112. // number of readers and disable writing.
  113. // ==================================================================
  114. PRINTF("%d got event\n", GetCurrentThreadId());
  115. EnterCriticalSection(&m_csAll);
  116. m_nReading++;
  117. m_adwReaders.Add((void*)dwThreadId);
  118. PRINTF("Reset write\n");
  119. ResetEvent(m_hCanWrite);
  120. PRINTF("Done\n");
  121. // Get out of all critical sections and return
  122. // ===========================================
  123. LeaveCriticalSection(&m_csAll);
  124. LeaveCriticalSection(&m_csEntering);
  125. PRINTF("%d begins to read\n", GetCurrentThreadId());
  126. return NoError;
  127. }
  128. //******************************************************************************
  129. //
  130. // See lock.h for documentation
  131. //
  132. //******************************************************************************
  133. int CLock::ReadUnlock()
  134. {
  135. PRINTF("%d wants to unlock reading\n", GetCurrentThreadId());
  136. // Enter internal ciritcal section and decrement the number of readers
  137. // ===================================================================
  138. EnterCriticalSection(&m_csAll);
  139. m_nReading--;
  140. if(m_nReading < 0) return Failed;
  141. // Remove it from the list of threads
  142. // ==================================
  143. DWORD_PTR dwThreadId = GetCurrentThreadId();
  144. for(int i = 0; i < m_adwReaders.Size(); i++)
  145. {
  146. if((DWORD_PTR)m_adwReaders[i] == dwThreadId)
  147. {
  148. m_adwReaders.RemoveAt(i);
  149. break;
  150. }
  151. }
  152. // If all reasders are gone, allow writers in
  153. // ==========================================
  154. if(m_nReading == 0)
  155. {
  156. PRINTF("%d is the last reader\n", GetCurrentThreadId());
  157. PRINTF("Set write\n");
  158. if(!SetEvent(m_hCanWrite))
  159. {
  160. LeaveCriticalSection(&m_csAll);
  161. return Failed;
  162. }
  163. PRINTF("Done\n");
  164. }
  165. else PRINTF("%d sees %d still reading\n", GetCurrentThreadId(), m_nReading);
  166. // Get out and return
  167. // ==================
  168. LeaveCriticalSection(&m_csAll);
  169. return NoError;
  170. }
  171. //******************************************************************************
  172. //
  173. // See lock.h for documentation
  174. //
  175. //******************************************************************************
  176. int CLock::WriteLock(DWORD dwTimeout)
  177. {
  178. PRINTF("%d wants to write\n", GetCurrentThreadId());
  179. // Get in line for getting any kind of lock. Those unlocking don't use this
  180. // critical section.
  181. // ========================================================================
  182. EnterCriticalSection(&m_csEntering);
  183. // We are the only ones allowed to get any kind of lock now
  184. // ========================================================
  185. PRINTF("%d next to enter\n", GetCurrentThreadId());
  186. // Wait for the event allowing writing to become signaled
  187. // ======================================================
  188. int nRes = WaitFor(m_hCanWrite, dwTimeout);
  189. PRINTF("%d got event\n", GetCurrentThreadId());
  190. if(nRes != NoError)
  191. {
  192. LeaveCriticalSection(&m_csEntering);
  193. return nRes;
  194. }
  195. // Enter internal critical section (unlockers use it too), increment the
  196. // number of writers (from 0 to 1) and disable both reading and writing
  197. // from now on.
  198. // ======================================================================
  199. EnterCriticalSection(&m_csAll);
  200. m_nWriting++;
  201. PRINTF("Reset both\n");
  202. ResetEvent(m_hCanWrite);
  203. ResetEvent(m_hCanRead);
  204. PRINTF("Done\n");
  205. // Get out and return
  206. // ==================
  207. LeaveCriticalSection(&m_csAll);
  208. LeaveCriticalSection(&m_csEntering);
  209. PRINTF("%d begins to write\n", GetCurrentThreadId());
  210. return NoError;
  211. }
  212. //******************************************************************************
  213. //
  214. // See lock.h for documentation
  215. //
  216. //******************************************************************************
  217. int CLock::WriteUnlock()
  218. {
  219. PRINTF("%d wants to release writing\n", GetCurrentThreadId());
  220. // Enter lock determination critical section
  221. // =========================================
  222. EnterCriticalSection(&m_csAll);
  223. m_nWriting--;
  224. if(m_nWriting < 0) return Failed;
  225. // Allow readers and writers in
  226. // ============================
  227. PRINTF("%d released writing\n", GetCurrentThreadId());
  228. PRINTF("Set both\n");
  229. if(!SetEvent(m_hCanRead))
  230. {
  231. LeaveCriticalSection(&m_csAll);
  232. return Failed;
  233. }
  234. else if(!SetEvent(m_hCanWrite))
  235. {
  236. LeaveCriticalSection(&m_csAll);
  237. return Failed;
  238. }
  239. else
  240. {
  241. PRINTF("Done\n");
  242. LeaveCriticalSection(&m_csAll);
  243. return NoError;
  244. }
  245. }
  246. //******************************************************************************
  247. //
  248. // See lock.h for documentation
  249. //
  250. //******************************************************************************
  251. int CLock::DowngradeLock()
  252. {
  253. // Enter lock determination critical section
  254. // =========================================
  255. EnterCriticalSection(&m_csAll);
  256. if(!SetEvent(m_hCanRead))
  257. {
  258. LeaveCriticalSection(&m_csAll);
  259. return Failed;
  260. }
  261. m_nReading++;
  262. LeaveCriticalSection(&m_csAll);
  263. return NoError;
  264. }
  265. //******************************************************************************
  266. //
  267. // See lock.h for documentation
  268. //
  269. //******************************************************************************
  270. int CLock::WaitFor(HANDLE hEvent, DWORD dwTimeout)
  271. {
  272. DWORD dwRes;
  273. dwRes = WaitForSingleObject(hEvent, dwTimeout);
  274. // Analyze the error code and convert to ours
  275. // ==========================================
  276. if(dwRes == WAIT_OBJECT_0) return NoError;
  277. else if(dwRes == WAIT_TIMEOUT) return TimedOut;
  278. else return Failed;
  279. }