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.

313 lines
7.3 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. /* File: thdsync.cpp
  3. Description: Contains classes for managing thread synchronization in
  4. Win32 programs. Most of the work is to provide automatic unlocking
  5. of synchronization primities on object destruction. The work on
  6. monitors and condition variables is strongly patterned after
  7. work in "Multithreaded Programming with Windows NT" by Pham and Garg.
  8. Revision History:
  9. Date Description Programmer
  10. -------- --------------------------------------------------- ----------
  11. 09/22/97 Initial creation. BrianAu
  12. */
  13. ///////////////////////////////////////////////////////////////////////////////
  14. #include "pch.h"
  15. #pragma hdrstop
  16. #include "thdsync.h"
  17. CSemaphore::CSemaphore(
  18. DWORD dwInitialCount,
  19. DWORD dwMaxCount
  20. ) : CWin32SyncObj(CreateSemaphore(NULL, dwInitialCount, dwMaxCount, NULL))
  21. {
  22. if (NULL == Handle())
  23. throw CSyncException(CSyncException::semaphore, CSyncException::create);
  24. }
  25. CSemaphoreList::~CSemaphoreList(
  26. void
  27. )
  28. {
  29. for (Item *pItem = m_pFirst; NULL != pItem; pItem = m_pFirst)
  30. {
  31. m_pFirst = m_pFirst->m_pNext;
  32. delete pItem->m_pSem;
  33. delete pItem;
  34. }
  35. }
  36. void
  37. CSemaphoreList::Dump(
  38. void
  39. )
  40. {
  41. DBGPRINT((TEXT("List: 0x%08X ------------------------------"), this));
  42. DBGPRINT((TEXT("m_pFirst = 0x%08X"), m_pFirst));
  43. DBGPRINT((TEXT("m_pLast = 0x%08X"), m_pLast));
  44. for (Item *pItem = m_pFirst; NULL != pItem; pItem = pItem->m_pNext)
  45. {
  46. DBGPRINT((TEXT("\tpItem = 0x%08X"), pItem));
  47. DBGPRINT((TEXT("\t\tm_pNext = 0x%08X"), pItem->m_pNext));
  48. DBGPRINT((TEXT("\t\tm_pSem = 0x%08X"), pItem->m_pSem));
  49. }
  50. }
  51. //
  52. // Insert a semaphore at the head of the list.
  53. //
  54. void
  55. CSemaphoreList::Prepend(
  56. CSemaphore *pSem
  57. )
  58. {
  59. m_pFirst = new Item(pSem, m_pFirst);
  60. if (NULL == m_pLast)
  61. {
  62. m_pLast = m_pFirst;
  63. }
  64. }
  65. //
  66. // Append a semaphore to the tail of the list.
  67. //
  68. void
  69. CSemaphoreList::Append(
  70. CSemaphore *pSem
  71. )
  72. {
  73. if (NULL == m_pFirst)
  74. {
  75. //
  76. // Empty list. Append is same as prepend.
  77. //
  78. Prepend(pSem);
  79. }
  80. else
  81. {
  82. //
  83. // Create and append the new item.
  84. //
  85. m_pLast->m_pNext = new Item(pSem);
  86. m_pLast = m_pLast->m_pNext;
  87. }
  88. }
  89. //
  90. // Remove item from the head of the list and return the
  91. // semaphore pointer held in the item. Delete the item.
  92. //
  93. CSemaphore *
  94. CSemaphoreList::Head(
  95. void
  96. )
  97. {
  98. CSemaphore *pSem = NULL;
  99. Item *pHead = m_pFirst; // Get first item.
  100. if (NULL != pHead)
  101. {
  102. pSem = pHead->m_pSem; // Get first item's semaphore ptr.
  103. m_pFirst = m_pFirst->m_pNext; // Unlink item from list.
  104. delete pHead; // Delete item.
  105. if (NULL == m_pFirst)
  106. m_pLast = NULL; // Adjust "last" ptr if necessary.
  107. }
  108. return pSem; // Return semaphore ptr.
  109. }
  110. CMutex::CMutex(
  111. BOOL bInitialOwner
  112. ) : CWin32SyncObj(CreateMutex(NULL, bInitialOwner, NULL))
  113. {
  114. if (NULL == Handle())
  115. throw CSyncException(CSyncException::mutex, CSyncException::create);
  116. }
  117. CEvent::CEvent(
  118. BOOL bManualReset,
  119. BOOL bInitialState
  120. ) : CWin32SyncObj(CreateEvent(NULL, bManualReset, bInitialState, NULL))
  121. {
  122. if (NULL == Handle())
  123. throw CSyncException(CSyncException::event, CSyncException::create);
  124. }
  125. //
  126. // Release a "signal-unrgent" monitor.
  127. //
  128. void
  129. CMonitorSU::Release(
  130. void
  131. )
  132. {
  133. if (0 != m_cUrgentSemCount)
  134. {
  135. //
  136. // There's more than one thread waiting on the "urgent" list.
  137. // Wake it up and let it run INSIDE of the monitor.
  138. //
  139. CSemaphore *pSem = m_UrgentSemList.Head();
  140. if (NULL != pSem)
  141. {
  142. pSem->Release();
  143. }
  144. }
  145. else
  146. {
  147. //
  148. // Exit the monitor.
  149. //
  150. m_Mutex.Release();
  151. }
  152. }
  153. //
  154. // Wait on a "signal-return" condition variable.
  155. //
  156. void
  157. CConditionSR::Wait(
  158. void
  159. )
  160. {
  161. m_cSemCount++; // One more thread waiting.
  162. m_Monitor.Release(); // Release monitor's lock to prevent deadlock.
  163. m_Sem.Wait(); // Block until condition is signaled.
  164. m_cSemCount--; // Have the lock, no longer waiting.
  165. }
  166. //
  167. // Signal a "signal-return" condition variable.
  168. //
  169. void
  170. CConditionSR::Signal(
  171. void
  172. )
  173. {
  174. //
  175. // If any threads blocked on the condition variable, release one
  176. // of them.
  177. //
  178. if (0 < m_cSemCount)
  179. m_Sem.Release(); // Release thd's blocked on semaphore.
  180. else
  181. m_Monitor.Release(); // Release monitor's lock.
  182. }
  183. //
  184. // Wait on a "signal-urgent" condition variable.
  185. //
  186. void
  187. CConditionSU::Wait(
  188. void
  189. )
  190. {
  191. CSemaphore *pSem = new CSemaphore;
  192. if (NULL != pSem)
  193. {
  194. //
  195. // Add the semaphore to the list of waiting threads.
  196. //
  197. m_SemList.Append(pSem);
  198. m_cSemCount++;
  199. if (0 != m_Monitor.m_cUrgentSemCount)
  200. {
  201. //
  202. // At least 1 thread on the "urgent" list. Get the one highest
  203. // priority and let it run while we wait.
  204. //
  205. CSemaphore *pUrgentItem = m_Monitor.m_UrgentSemList.Head();
  206. if (NULL != pUrgentItem)
  207. {
  208. pUrgentItem->Release();
  209. }
  210. }
  211. else
  212. {
  213. //
  214. // Exit the monitor.
  215. //
  216. m_Monitor.Release();
  217. }
  218. //
  219. // Wait for this condition variable to be signaled.
  220. //
  221. pSem->Wait();
  222. m_cSemCount--;
  223. delete pSem;
  224. }
  225. }
  226. //
  227. // Signal a "signal-urgent" condition variable.
  228. //
  229. void
  230. CConditionSU::Signal(
  231. void
  232. )
  233. {
  234. if (0 < m_cSemCount)
  235. {
  236. //
  237. // At least 1 thread waiting on this condition variable.
  238. //
  239. CSemaphore *pSemNew = new CSemaphore;
  240. if (NULL != pSemNew)
  241. {
  242. //
  243. // Add a new semaphore to the "urgent" list.
  244. //
  245. m_Monitor.m_UrgentSemList.Prepend(pSemNew);
  246. m_Monitor.m_cUrgentSemCount++;
  247. //
  248. // Retrieve the next semaphore from the condition list and
  249. // release it allowing it's thread to run.
  250. //
  251. CSemaphore *pSemFromList = m_SemList.Head();
  252. if (NULL != pSemFromList)
  253. {
  254. pSemFromList->Release();
  255. }
  256. //
  257. // Wait for this condition variable to be signaled.
  258. //
  259. pSemNew->Wait();
  260. m_Monitor.m_cUrgentSemCount--;
  261. delete pSemNew;
  262. }
  263. }
  264. }
  265. //
  266. // Wait on a Win32 mutex object.
  267. // Throw an exception if the mutex has been abandoned or the wait has timed out.
  268. //
  269. void
  270. AutoLockMutex::Wait(
  271. DWORD dwTimeout
  272. )
  273. {
  274. DWORD dwStatus = WaitForSingleObject(m_hMutex, dwTimeout);
  275. switch(dwStatus)
  276. {
  277. case WAIT_ABANDONED:
  278. throw CSyncException(CSyncException::mutex, CSyncException::abandoned);
  279. break;
  280. case WAIT_TIMEOUT:
  281. throw CSyncException(CSyncException::mutex, CSyncException::timeout);
  282. break;
  283. default:
  284. break;
  285. }
  286. }