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