123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
- // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
- // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
- // PARTICULAR PURPOSE.
- //
- // Copyright (c) Microsoft Corporation. All rights reserved.
- #include "ThreadEmulation.h"
- #include <assert.h>
- #include <vector>
- #include <set>
- #include <map>
- #include <mutex>
- using namespace std;
- using namespace Platform;
- using namespace Windows::Foundation;
- using namespace Windows::System::Threading;
- namespace ThreadEmulation
- {
- // Stored data for CREATE_SUSPENDED and ResumeThread.
- struct PendingThreadInfo {
- LPTHREAD_START_ROUTINE lpStartAddress;
- LPVOID lpParameter;
- HANDLE completionEvent;
- int nPriority;
- };
- static map<HANDLE, PendingThreadInfo> pendingThreads;
- static mutex pendingThreadsLock;
- // Thread local storage.
- typedef vector<void*> ThreadLocalData;
- static __declspec(thread) ThreadLocalData* currentThreadData = nullptr;
- static set<ThreadLocalData*> allThreadData;
- static DWORD nextTlsIndex = 0;
- static vector<DWORD> freeTlsIndices;
- static mutex tlsAllocationLock;
- // Converts a Win32 thread priority to WinRT format.
- static WorkItemPriority GetWorkItemPriority(int nPriority)
- {
- if (nPriority < 0) {
- return WorkItemPriority::Low;
- }
- else if (nPriority > 0) {
- return WorkItemPriority::High;
- }
- else {
- return WorkItemPriority::Normal;
- }
- }
- // Helper shared between CreateThread and ResumeThread.
- static void StartThread(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, HANDLE completionEvent, int nPriority)
- {
- auto workItemHandler = ref new WorkItemHandler([=](IAsyncAction^) {
- // Run the user callback.
- try {
- lpStartAddress(lpParameter);
- }
- catch (...) { }
- // Clean up any TLS allocations made by this thread.
- TlsShutdown();
- // Signal that the thread has completed.
- SetEvent(completionEvent);
- CloseHandle(completionEvent);
- }, CallbackContext::Any);
- ThreadPool::RunAsync(workItemHandler, GetWorkItemPriority(nPriority), WorkItemOptions::TimeSliced);
- }
- _Use_decl_annotations_ HANDLE WINAPI CreateThread(LPSECURITY_ATTRIBUTES unusedThreadAttributes, SIZE_T unusedStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD unusedThreadId)
- {
- // Validate parameters.
- assert(unusedThreadAttributes == nullptr);
- assert(unusedStackSize == 0);
- assert((dwCreationFlags & ~CREATE_SUSPENDED) == 0);
- assert(unusedThreadId == nullptr);
- // Create a handle that will be signalled when the thread has completed.
- HANDLE threadHandle = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
- if (!threadHandle) {
- return nullptr;
- }
- // Make a copy of the handle for internal use. This is necessary because
- // the caller is responsible for closing the handle returned by CreateThread,
- // and they may do that before or after the thread has finished running.
- HANDLE completionEvent;
- if (!DuplicateHandle(GetCurrentProcess(), threadHandle, GetCurrentProcess(), &completionEvent, 0, false, DUPLICATE_SAME_ACCESS)) {
- CloseHandle(threadHandle);
- return nullptr;
- }
- try {
- if (dwCreationFlags & CREATE_SUSPENDED) {
- // Store info about a suspended thread.
- PendingThreadInfo info;
- info.lpStartAddress = lpStartAddress;
- info.lpParameter = lpParameter;
- info.completionEvent = completionEvent;
- info.nPriority = 0;
- lock_guard<mutex> lock(pendingThreadsLock);
- pendingThreads[threadHandle] = info;
- }
- else {
- // Start the thread immediately.
- StartThread(lpStartAddress, lpParameter, completionEvent, 0);
- }
- return threadHandle;
- }
- catch (...) {
- // Clean up if thread creation fails.
- CloseHandle(threadHandle);
- CloseHandle(completionEvent);
- return nullptr;
- }
- }
- _Use_decl_annotations_ DWORD WINAPI ResumeThread(HANDLE hThread)
- {
- lock_guard<mutex> lock(pendingThreadsLock);
- // Look up the requested thread.
- auto threadInfo = pendingThreads.find(hThread);
- if (threadInfo == pendingThreads.end()) {
- // Can only resume threads while they are in CREATE_SUSPENDED state.
- assert(false);
- return (DWORD)-1;
- }
- // Start the thread.
- try {
- PendingThreadInfo& info = threadInfo->second;
- StartThread(info.lpStartAddress, info.lpParameter, info.completionEvent, info.nPriority);
- }
- catch (...) {
- return (DWORD)-1;
- }
- // Remove this thread from the pending list.
- pendingThreads.erase(threadInfo);
- return 0;
- }
- _Use_decl_annotations_ BOOL WINAPI SetThreadPriority(HANDLE hThread, int nPriority)
- {
- lock_guard<mutex> lock(pendingThreadsLock);
- // Look up the requested thread.
- auto threadInfo = pendingThreads.find(hThread);
- if (threadInfo == pendingThreads.end()) {
- // Can only set priority on threads while they are in CREATE_SUSPENDED state.
- return false;
- }
- // Store the new priority.
- threadInfo->second.nPriority = nPriority;
- return true;
- }
- _Use_decl_annotations_ VOID WINAPI Sleep(DWORD dwMilliseconds)
- {
- static HANDLE singletonEvent = nullptr;
- HANDLE sleepEvent = singletonEvent;
- // Demand create the event.
- if (!sleepEvent) {
- sleepEvent = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
- if (!sleepEvent) {
- return;
- }
- HANDLE previousEvent = InterlockedCompareExchangePointerRelease(&singletonEvent, sleepEvent, nullptr);
- if (previousEvent) {
- // Back out if multiple threads try to demand create at the same time.
- CloseHandle(sleepEvent);
- sleepEvent = previousEvent;
- }
- }
- // Emulate sleep by waiting with timeout on an event that is never signalled.
- WaitForSingleObjectEx(sleepEvent, dwMilliseconds, false);
- }
- DWORD WINAPI TlsAlloc()
- {
- lock_guard<mutex> lock(tlsAllocationLock);
- // Can we reuse a previously freed TLS slot?
- if (!freeTlsIndices.empty()) {
- DWORD result = freeTlsIndices.back();
- freeTlsIndices.pop_back();
- return result;
- }
- // Allocate a new TLS slot.
- return nextTlsIndex++;
- }
- _Use_decl_annotations_ BOOL WINAPI TlsFree(DWORD dwTlsIndex)
- {
- lock_guard<mutex> lock(tlsAllocationLock);
- assert(dwTlsIndex < nextTlsIndex);
- assert(find(freeTlsIndices.begin(), freeTlsIndices.end(), dwTlsIndex) == freeTlsIndices.end());
- // Store this slot for reuse by TlsAlloc.
- try {
- freeTlsIndices.push_back(dwTlsIndex);
- }
- catch (...) {
- return false;
- }
- // Zero the value for all threads that might be using this now freed slot.
- for each (auto threadData in allThreadData) {
- if (threadData->size() > dwTlsIndex) {
- threadData->at(dwTlsIndex) = nullptr;
- }
- }
- return true;
- }
- _Use_decl_annotations_ LPVOID WINAPI TlsGetValue(DWORD dwTlsIndex)
- {
- ThreadLocalData* threadData = currentThreadData;
- if (threadData && threadData->size() > dwTlsIndex) {
- // Return the value of an allocated TLS slot.
- return threadData->at(dwTlsIndex);
- }
- else {
- // Default value for unallocated slots.
- return nullptr;
- }
- }
- _Use_decl_annotations_ BOOL WINAPI TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue)
- {
- ThreadLocalData* threadData = currentThreadData;
- if (!threadData) {
- // First time allocation of TLS data for this thread.
- try {
- threadData = new ThreadLocalData(dwTlsIndex + 1, nullptr);
- lock_guard<mutex> lock(tlsAllocationLock);
- allThreadData.insert(threadData);
- currentThreadData = threadData;
- }
- catch (...) {
- if (threadData) {
- delete threadData;
- }
- return false;
- }
- }
- else if (threadData->size() <= dwTlsIndex) {
- // This thread already has a TLS data block, but it must be expanded to fit the specified slot.
- try {
- lock_guard<mutex> lock(tlsAllocationLock);
- threadData->resize(dwTlsIndex + 1, nullptr);
- }
- catch (...) {
- return false;
- }
- }
- // Store the new value for this slot.
- threadData->at(dwTlsIndex) = lpTlsValue;
- return true;
- }
- // Called at thread exit to clean up TLS allocations.
- void WINAPI TlsShutdown()
- {
- ThreadLocalData* threadData = currentThreadData;
- if (threadData) {
- {
- lock_guard<mutex> lock(tlsAllocationLock);
- allThreadData.erase(threadData);
- }
- currentThreadData = nullptr;
- delete threadData;
- }
- }
- }
|