dsi_thread_win32.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*
  2. This software is subject to the license described in the License.txt file
  3. included with this software distribution. You may not use this file except
  4. in compliance with this license.
  5. Copyright (c) Dynastream Innovations Inc. 2016
  6. All rights reserved.
  7. */
  8. #include "types.h"
  9. #if defined(DSI_TYPES_WINDOWS)
  10. #include "dsi_thread.h"
  11. #include "macros.h"
  12. #include <windows.h>
  13. #include <string.h>
  14. //////////////////////////////////////////////////////////////////////////////////
  15. // Public Functions
  16. //////////////////////////////////////////////////////////////////////////////////
  17. ///////////////////////////////////////////////////////////////////////
  18. // Code below is no longer based on "Strategies for Implementing POSIX Condition
  19. // Variables on Win32" by Douglas C. Schmidt and Irfan Pyarali of
  20. // Department of Computer Science, Washington University, St. Louis,
  21. // Missouri
  22. // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
  23. // This implementation had some flaws that were causing fals
  24. ///////////////////////////////////////////////////////////////////////
  25. ///////////////////////////////////////////////////////////////////////
  26. UCHAR DSIThread_MutexInit(DSI_MUTEX *pstMutex_)
  27. {
  28. *pstMutex_ = CreateMutex(
  29. (LPSECURITY_ATTRIBUTES) NULL, // This pstMutex_ gets the default security descriptor.
  30. FALSE, // Not owned by the calling thread; should be "unlocked".
  31. (LPCTSTR) NULL); // Object name not required.
  32. if (*pstMutex_ == NULL)
  33. return DSI_THREAD_EOTHER; // We've failed for some reason.
  34. return DSI_THREAD_ENONE;
  35. }
  36. ///////////////////////////////////////////////////////////////////////
  37. UCHAR DSIThread_MutexDestroy(DSI_MUTEX *pstMutex_)
  38. {
  39. if (CloseHandle(*pstMutex_))
  40. return DSI_THREAD_ENONE;
  41. return DSI_THREAD_EOTHER;
  42. }
  43. ///////////////////////////////////////////////////////////////////////
  44. UCHAR DSIThread_MutexLock(DSI_MUTEX *pstMutex_)
  45. {
  46. WaitForSingleObject(*pstMutex_, INFINITE);
  47. return DSI_THREAD_ENONE;
  48. }
  49. ///////////////////////////////////////////////////////////////////////
  50. UCHAR DSIThread_MutexTryLock(DSI_MUTEX *pstMutex_)
  51. {
  52. DWORD dwWaitObject = WaitForSingleObject(*pstMutex_, 0);
  53. if (dwWaitObject == WAIT_OBJECT_0)
  54. return DSI_THREAD_ENONE;
  55. return DSI_THREAD_EBUSY;
  56. }
  57. ///////////////////////////////////////////////////////////////////////
  58. UCHAR DSIThread_MutexUnlock(DSI_MUTEX *pstMutex_)
  59. {
  60. ReleaseMutex(*pstMutex_);
  61. return DSI_THREAD_ENONE;
  62. }
  63. ///////////////////////////////////////////////////////////////////////
  64. UCHAR DSIThread_CondInit(DSI_CONDITION_VAR *pstConditionVariable_)
  65. {
  66. pstConditionVariable_->iWaitersCount = 0;
  67. InitializeCriticalSection(&pstConditionVariable_->stStartWaiterCritSec);
  68. //Create the broadcast semaphore
  69. pstConditionVariable_->ahBcastSmph0SignalEvnt1[0] = CreateSemaphore(
  70. (LPSECURITY_ATTRIBUTES) NULL, // This semaphore gets the default security descriptor.
  71. 0, // Initially 0.
  72. 0x7FFFFFFF, // Max count.
  73. (LPCTSTR) NULL); // Object name not required.
  74. //Create the 'signal one' event
  75. pstConditionVariable_->ahBcastSmph0SignalEvnt1[1] = CreateEvent (
  76. (LPSECURITY_ATTRIBUTES) NULL, // This event gets the default security descriptor.
  77. FALSE, // FALSE means we use auto-reset.
  78. FALSE, // Non-signaled initially.
  79. (LPCTSTR) NULL); // Object name not required.
  80. pstConditionVariable_->hBroadcastDoneEvent = CreateEvent (
  81. (LPSECURITY_ATTRIBUTES) NULL, // This event gets the default security descriptor.
  82. FALSE, // FALSE means we use auto-reset.
  83. FALSE, // Non-signaled initially.
  84. (LPCTSTR) NULL); // Object name not required.
  85. if (pstConditionVariable_->ahBcastSmph0SignalEvnt1[0] == NULL
  86. || pstConditionVariable_->ahBcastSmph0SignalEvnt1[1] == NULL
  87. || pstConditionVariable_->hBroadcastDoneEvent == NULL)
  88. return DSI_THREAD_EOTHER; // We've failed initializing objects for some reason, call GetLastError() to figure out why
  89. return DSI_THREAD_ENONE;
  90. }
  91. ///////////////////////////////////////////////////////////////////////
  92. UCHAR DSIThread_CondDestroy(DSI_CONDITION_VAR *pstConditionVariable_)
  93. {
  94. UCHAR ucRetVal = DSI_THREAD_ENONE;
  95. EnterCriticalSection(&pstConditionVariable_->stStartWaiterCritSec); //Make sure we have ownership before we kill it
  96. DeleteCriticalSection(&pstConditionVariable_->stStartWaiterCritSec); //Now nobody can get it, if someone is still waiting they will be stuck, but it is better than calling LeaveCriticalSection on a deleted cs (which causes possible memory corruption).
  97. //Release all threads that are stuck waiting so we try not to leave any dead waits
  98. ReleaseSemaphore(pstConditionVariable_->ahBcastSmph0SignalEvnt1[0], 0x7fffffff, 0);
  99. //Destroy the rest of the objects
  100. if (!CloseHandle(pstConditionVariable_->hBroadcastDoneEvent))
  101. ucRetVal = DSI_THREAD_EOTHER;
  102. if (!CloseHandle(pstConditionVariable_->ahBcastSmph0SignalEvnt1[1]))
  103. ucRetVal = DSI_THREAD_EOTHER;
  104. if (!CloseHandle(pstConditionVariable_->ahBcastSmph0SignalEvnt1[0]))
  105. ucRetVal = DSI_THREAD_EOTHER;
  106. return ucRetVal;
  107. }
  108. ///////////////////////////////////////////////////////////////////////
  109. UCHAR DSIThread_CondTimedWait(DSI_CONDITION_VAR *pstConditionVariable_, DSI_MUTEX *pstExternalMutex_, ULONG ulMilliseconds_)
  110. {
  111. DWORD dwResult;
  112. //Make sure DSI_THREAD_INFINITE translates to INFINITE
  113. if(ulMilliseconds_ == DSI_THREAD_INFINITE)
  114. ulMilliseconds_ = INFINITE;
  115. // Add a count for us when we are allowed to start waiting
  116. EnterCriticalSection(&pstConditionVariable_->stStartWaiterCritSec);
  117. InterlockedIncrement(&(pstConditionVariable_->iWaitersCount));
  118. LeaveCriticalSection(&pstConditionVariable_->stStartWaiterCritSec);
  119. //Release the variable mutex to allow whatever will signal us (which needs to acquire the mutex) to occur
  120. if(ReleaseMutex(*pstExternalMutex_) == 0)
  121. return DSI_THREAD_EOTHER;
  122. //Wait for broadcast or normal signal
  123. dwResult = WaitForMultipleObjects(2, pstConditionVariable_->ahBcastSmph0SignalEvnt1, FALSE, ulMilliseconds_);
  124. if(dwResult - WAIT_OBJECT_0 == 1) //array index is the normal signal event
  125. {
  126. //Note: The waiter count is already decremented by the signal function
  127. dwResult = WaitForSingleObject(*pstExternalMutex_, INFINITE);
  128. }
  129. else if(dwResult >= WAIT_ABANDONED) //If we didn't get a signal
  130. {
  131. //Even though we have an error, the contract states we need the mutex back before we return
  132. //Also, since we didn't get a signal, we need to manage the waiterCount here
  133. //Lastly, If we were in a broadcast and are the last waiter, we need to ensure the signal is fired, and
  134. //it doesn't hurt to do it out of a broadcast
  135. //Note: We don't handle this the same as the broadcast case, since we want to return the original failure code, not save a new one
  136. if(InterlockedDecrement(&(pstConditionVariable_->iWaitersCount)) == 0)
  137. SignalObjectAndWait(pstConditionVariable_->hBroadcastDoneEvent, *pstExternalMutex_, INFINITE, FALSE);
  138. else
  139. WaitForSingleObject(*pstExternalMutex_, INFINITE);
  140. }
  141. else //dwResult - WAIT_OBJECT_0 == 0 //array index is the broadcast event
  142. {
  143. //Decrement waiter count if we are in broadcast,
  144. //If we are the last waiter we need to signal the broadcast is done
  145. //Interlocked, so value is correct between all broadcast released threads
  146. if(InterlockedDecrement(&(pstConditionVariable_->iWaitersCount)) == 0)
  147. dwResult = SignalObjectAndWait(pstConditionVariable_->hBroadcastDoneEvent, *pstExternalMutex_, INFINITE, FALSE);
  148. else
  149. dwResult = WaitForSingleObject(*pstExternalMutex_, INFINITE);
  150. }
  151. if (dwResult == WAIT_OBJECT_0)
  152. return DSI_THREAD_ENONE;
  153. if (dwResult == WAIT_TIMEOUT)
  154. return DSI_THREAD_ETIMEDOUT;
  155. return DSI_THREAD_EOTHER;
  156. }
  157. ///////////////////////////////////////////////////////////////////////
  158. UCHAR DSIThread_CondSignal(DSI_CONDITION_VAR *pstConditionVariable_)
  159. {
  160. //We get the 'start' mutex to ensure we aren't signalling during a broadcast
  161. EnterCriticalSection(&pstConditionVariable_->stStartWaiterCritSec);
  162. //CondSignal allows one current waiter through, so only open the event if we have current waiters
  163. //We decrement waiters count here in signal to avoid race condition where signal could occur multiple
  164. //times between last waiter thread being signaled and them decrementing their own waiter count (broadcast case is different)
  165. if(pstConditionVariable_->iWaitersCount > 0)
  166. {
  167. InterlockedDecrement(&(pstConditionVariable_->iWaitersCount));
  168. SetEvent(pstConditionVariable_->ahBcastSmph0SignalEvnt1[1]);
  169. }
  170. LeaveCriticalSection(&pstConditionVariable_->stStartWaiterCritSec);
  171. return DSI_THREAD_ENONE;
  172. }
  173. ///////////////////////////////////////////////////////////////////////
  174. UCHAR DSIThread_CondBroadcast(DSI_CONDITION_VAR *pstConditionVariable_)
  175. {
  176. // We need the waiter lock to ensure the broadcast only signals the current waiters
  177. // This also blocks CondSignal() from firing
  178. // This lock also ensures the normal signal doesn't happen during the broadcast and mess up the count
  179. EnterCriticalSection(&pstConditionVariable_->stStartWaiterCritSec);
  180. if (pstConditionVariable_->iWaitersCount > 0)
  181. {
  182. // Release all the current waiters using the broadcast semaphore
  183. ReleaseSemaphore(pstConditionVariable_->ahBcastSmph0SignalEvnt1[0], pstConditionVariable_->iWaitersCount, 0);
  184. // Wait until the last one is done
  185. WaitForSingleObject(pstConditionVariable_->hBroadcastDoneEvent, INFINITE);
  186. }
  187. //Release the lock to allow new waiters again
  188. LeaveCriticalSection(&pstConditionVariable_->stStartWaiterCritSec);
  189. return DSI_THREAD_ENONE;
  190. }
  191. ///////////////////////////////////////////////////////////////////////
  192. DSI_THREAD_ID DSIThread_CreateThread(DSI_THREAD_RETURN (*fnThreadStart_)(void *), void *pvParameter_)
  193. {
  194. return
  195. CreateThread(
  196. (LPSECURITY_ATTRIBUTES) NULL, // Default security descriptor.
  197. 0, // Default stack size for exe.
  198. (LPTHREAD_START_ROUTINE) fnThreadStart_,
  199. pvParameter_,
  200. 0, // The thread begins execution immediately.
  201. NULL); // No need for a thread ID.
  202. }
  203. ///////////////////////////////////////////////////////////////////////
  204. UCHAR DSIThread_DestroyThread(DSI_THREAD_ID hThreadID_)
  205. {
  206. if(TerminateThread(hThreadID_, 0) == 0)
  207. return DSI_THREAD_EOTHER;
  208. return DSI_THREAD_ENONE;
  209. }
  210. ///////////////////////////////////////////////////////////////////////
  211. UCHAR DSIThread_ReleaseThreadID(DSI_THREAD_ID hThreadID)
  212. {
  213. if(CloseHandle(hThreadID) == 0)
  214. return DSI_THREAD_EOTHER;
  215. return DSI_THREAD_ENONE;
  216. }
  217. ///////////////////////////////////////////////////////////////////////
  218. DSI_THREAD_IDNUM DSIThread_GetCurrentThreadIDNum(void)
  219. {
  220. return GetCurrentThreadId();
  221. }
  222. ///////////////////////////////////////////////////////////////////////
  223. BOOL DSIThread_CompareThreads(DSI_THREAD_IDNUM hThreadIDNum1, DSI_THREAD_IDNUM hThreadIDNum2)
  224. {
  225. return (hThreadIDNum1 == hThreadIDNum2);
  226. }
  227. ///////////////////////////////////////////////////////////////////////
  228. ULONG DSIThread_GetSystemTime(void)
  229. {
  230. return GetTickCount();
  231. }
  232. ///////////////////////////////////////////////////////////////////////
  233. BOOL DSIThread_GetWorkingDirectory(UCHAR* pucDirectory_, USHORT usLength_)
  234. {
  235. if(pucDirectory_ == NULL)
  236. return FALSE;
  237. if(GetCurrentDirectory((DWORD)(usLength_-1), (LPSTR)pucDirectory_) == 0)
  238. return FALSE;
  239. SNPRINTF((char*)(&pucDirectory_[strlen((char*)pucDirectory_)]), 2, "\\");
  240. return TRUE;
  241. }
  242. ///////////////////////////////////////////////////////////////////////
  243. void DSIThread_Sleep(ULONG ulMilliseconds_)
  244. {
  245. Sleep(ulMilliseconds_);
  246. return;
  247. }
  248. #endif //defined(DSI_TYPES_WINDOWS)