ThreadEmulation.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright (c) Microsoft Corporation. All rights reserved.
  7. #include "../include/ThreadEmulation.h"
  8. #include <assert.h>
  9. #include <vector>
  10. #include <set>
  11. #include <map>
  12. #include <mutex>
  13. using namespace std;
  14. using namespace Platform;
  15. using namespace Windows::Foundation;
  16. using namespace Windows::System::Threading;
  17. //namespace ThreadEmulation
  18. //{
  19. // Stored data for CREATE_SUSPENDED and ResumeThread.
  20. struct PendingThreadInfo
  21. {
  22. LPTHREAD_START_ROUTINE lpStartAddress;
  23. LPVOID lpParameter;
  24. HANDLE completionEvent;
  25. int nPriority;
  26. };
  27. static map<HANDLE, PendingThreadInfo> pendingThreads;
  28. static mutex pendingThreadsLock;
  29. // Thread local storage.
  30. typedef vector<void*> ThreadLocalData;
  31. static __declspec(thread) ThreadLocalData* currentThreadData = nullptr;
  32. static set<ThreadLocalData*> allThreadData;
  33. static DWORD nextTlsIndex = 0;
  34. static vector<DWORD> freeTlsIndices;
  35. static mutex tlsAllocationLock;
  36. // Converts a Win32 thread priority to WinRT format.
  37. static WorkItemPriority GetWorkItemPriority(int nPriority)
  38. {
  39. if (nPriority < 0)
  40. return WorkItemPriority::Low;
  41. else if (nPriority > 0)
  42. return WorkItemPriority::High;
  43. else
  44. return WorkItemPriority::Normal;
  45. }
  46. // Helper shared between CreateThread and ResumeThread.
  47. static void StartThread(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, HANDLE completionEvent, int nPriority)
  48. {
  49. auto workItemHandler = ref new WorkItemHandler([=](IAsyncAction^)
  50. {
  51. // Run the user callback.
  52. try
  53. {
  54. lpStartAddress(lpParameter);
  55. }
  56. catch (...) { }
  57. // Clean up any TLS allocations made by this thread.
  58. TlsShutdown();
  59. // Signal that the thread has completed.
  60. SetEvent(completionEvent);
  61. CloseHandle(completionEvent);
  62. }, CallbackContext::Any);
  63. ThreadPool::RunAsync(workItemHandler, GetWorkItemPriority(nPriority), WorkItemOptions::TimeSliced);
  64. }
  65. _Use_decl_annotations_ HANDLE WINAPI CreateThreadRT(LPSECURITY_ATTRIBUTES unusedThreadAttributes, SIZE_T unusedStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD unusedThreadId)
  66. {
  67. // Validate parameters.
  68. assert(unusedThreadAttributes == nullptr);
  69. assert(unusedStackSize == 0);
  70. assert((dwCreationFlags & ~CREATE_SUSPENDED) == 0);
  71. assert(unusedThreadId == nullptr);
  72. // Create a handle that will be signalled when the thread has completed.
  73. HANDLE threadHandle = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
  74. if (!threadHandle)
  75. return nullptr;
  76. // Make a copy of the handle for internal use. This is necessary because
  77. // the caller is responsible for closing the handle returned by CreateThread,
  78. // and they may do that before or after the thread has finished running.
  79. HANDLE completionEvent;
  80. if (!DuplicateHandle(GetCurrentProcess(), threadHandle, GetCurrentProcess(), &completionEvent, 0, false, DUPLICATE_SAME_ACCESS))
  81. {
  82. CloseHandle(threadHandle);
  83. return nullptr;
  84. }
  85. try
  86. {
  87. if (dwCreationFlags & CREATE_SUSPENDED)
  88. {
  89. // Store info about a suspended thread.
  90. PendingThreadInfo info;
  91. info.lpStartAddress = lpStartAddress;
  92. info.lpParameter = lpParameter;
  93. info.completionEvent = completionEvent;
  94. info.nPriority = 0;
  95. lock_guard<mutex> lock(pendingThreadsLock);
  96. pendingThreads[threadHandle] = info;
  97. }
  98. else
  99. {
  100. // Start the thread immediately.
  101. StartThread(lpStartAddress, lpParameter, completionEvent, 0);
  102. }
  103. return threadHandle;
  104. }
  105. catch (...)
  106. {
  107. // Clean up if thread creation fails.
  108. CloseHandle(threadHandle);
  109. CloseHandle(completionEvent);
  110. return nullptr;
  111. }
  112. }
  113. _Use_decl_annotations_ DWORD WINAPI ResumeThreadRT(HANDLE hThread)
  114. {
  115. lock_guard<mutex> lock(pendingThreadsLock);
  116. // Look up the requested thread.
  117. auto threadInfo = pendingThreads.find(hThread);
  118. if (threadInfo == pendingThreads.end())
  119. {
  120. // Can only resume threads while they are in CREATE_SUSPENDED state.
  121. assert(false);
  122. return (DWORD)-1;
  123. }
  124. // Start the thread.
  125. try
  126. {
  127. PendingThreadInfo& info = threadInfo->second;
  128. StartThread(info.lpStartAddress, info.lpParameter, info.completionEvent, info.nPriority);
  129. }
  130. catch (...)
  131. {
  132. return (DWORD)-1;
  133. }
  134. // Remove this thread from the pending list.
  135. pendingThreads.erase(threadInfo);
  136. return 0;
  137. }
  138. _Use_decl_annotations_ BOOL WINAPI SetThreadPriorityRT(HANDLE hThread, int nPriority)
  139. {
  140. lock_guard<mutex> lock(pendingThreadsLock);
  141. // Look up the requested thread.
  142. auto threadInfo = pendingThreads.find(hThread);
  143. if (threadInfo == pendingThreads.end())
  144. {
  145. // Can only set priority on threads while they are in CREATE_SUSPENDED state.
  146. assert(false);
  147. return false;
  148. }
  149. // Store the new priority.
  150. threadInfo->second.nPriority = nPriority;
  151. return true;
  152. }
  153. _Use_decl_annotations_ VOID WINAPI SleepRT(DWORD dwMilliseconds)
  154. {
  155. static HANDLE singletonEvent = nullptr;
  156. HANDLE sleepEvent = singletonEvent;
  157. // Demand create the event.
  158. if (!sleepEvent)
  159. {
  160. sleepEvent = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
  161. if (!sleepEvent)
  162. return;
  163. HANDLE previousEvent = InterlockedCompareExchangePointerRelease(&singletonEvent, sleepEvent, nullptr);
  164. if (previousEvent)
  165. {
  166. // Back out if multiple threads try to demand create at the same time.
  167. CloseHandle(sleepEvent);
  168. sleepEvent = previousEvent;
  169. }
  170. }
  171. // Emulate sleep by waiting with timeout on an event that is never signalled.
  172. WaitForSingleObjectEx(sleepEvent, dwMilliseconds, false);
  173. }
  174. DWORD WINAPI TlsAllocRT()
  175. {
  176. lock_guard<mutex> lock(tlsAllocationLock);
  177. // Can we reuse a previously freed TLS slot?
  178. if (!freeTlsIndices.empty())
  179. {
  180. DWORD result = freeTlsIndices.back();
  181. freeTlsIndices.pop_back();
  182. return result;
  183. }
  184. // Allocate a new TLS slot.
  185. return nextTlsIndex++;
  186. }
  187. _Use_decl_annotations_ BOOL WINAPI TlsFreeRT(DWORD dwTlsIndex)
  188. {
  189. lock_guard<mutex> lock(tlsAllocationLock);
  190. assert(dwTlsIndex < nextTlsIndex);
  191. assert(find(freeTlsIndices.begin(), freeTlsIndices.end(), dwTlsIndex) == freeTlsIndices.end());
  192. // Store this slot for reuse by TlsAlloc.
  193. try
  194. {
  195. freeTlsIndices.push_back(dwTlsIndex);
  196. }
  197. catch (...)
  198. {
  199. return false;
  200. }
  201. // Zero the value for all threads that might be using this now freed slot.
  202. for each (auto threadData in allThreadData)
  203. {
  204. if (threadData->size() > dwTlsIndex)
  205. {
  206. threadData->at(dwTlsIndex) = nullptr;
  207. }
  208. }
  209. return true;
  210. }
  211. _Use_decl_annotations_ LPVOID WINAPI TlsGetValueRT(DWORD dwTlsIndex)
  212. {
  213. ThreadLocalData* threadData = currentThreadData;
  214. if (threadData && threadData->size() > dwTlsIndex)
  215. {
  216. // Return the value of an allocated TLS slot.
  217. return threadData->at(dwTlsIndex);
  218. }
  219. else
  220. {
  221. // Default value for unallocated slots.
  222. return nullptr;
  223. }
  224. }
  225. _Use_decl_annotations_ BOOL WINAPI TlsSetValueRT(DWORD dwTlsIndex, LPVOID lpTlsValue)
  226. {
  227. ThreadLocalData* threadData = currentThreadData;
  228. if (!threadData)
  229. {
  230. // First time allocation of TLS data for this thread.
  231. try
  232. {
  233. threadData = new ThreadLocalData(dwTlsIndex + 1, nullptr);
  234. lock_guard<mutex> lock(tlsAllocationLock);
  235. allThreadData.insert(threadData);
  236. currentThreadData = threadData;
  237. }
  238. catch (...)
  239. {
  240. if (threadData)
  241. delete threadData;
  242. return false;
  243. }
  244. }
  245. else if (threadData->size() <= dwTlsIndex)
  246. {
  247. // This thread already has a TLS data block, but it must be expanded to fit the specified slot.
  248. try
  249. {
  250. lock_guard<mutex> lock(tlsAllocationLock);
  251. threadData->resize(dwTlsIndex + 1, nullptr);
  252. }
  253. catch (...)
  254. {
  255. return false;
  256. }
  257. }
  258. // Store the new value for this slot.
  259. threadData->at(dwTlsIndex) = lpTlsValue;
  260. return true;
  261. }
  262. // Called at thread exit to clean up TLS allocations.
  263. void WINAPI TlsShutdown()
  264. {
  265. ThreadLocalData* threadData = currentThreadData;
  266. if (threadData)
  267. {
  268. {
  269. lock_guard<mutex> lock(tlsAllocationLock);
  270. allThreadData.erase(threadData);
  271. }
  272. currentThreadData = nullptr;
  273. delete threadData;
  274. }
  275. }
  276. //}