Init
This commit is contained in:
360
DependentExtensions/WinPhone8/ThreadEmulation.cpp
Normal file
360
DependentExtensions/WinPhone8/ThreadEmulation.cpp
Normal file
@ -0,0 +1,360 @@
|
||||
// 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 (Exception ^e)
|
||||
{
|
||||
Platform::String ^exceptionString = e->ToString();
|
||||
OutputDebugStringW(exceptionString->Data());
|
||||
}
|
||||
|
||||
// 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.
|
||||
assert(false);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
DependentExtensions/WinPhone8/ThreadEmulation.h
Normal file
53
DependentExtensions/WinPhone8/ThreadEmulation.h
Normal file
@ -0,0 +1,53 @@
|
||||
// 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.
|
||||
//
|
||||
//
|
||||
// Emulates a subset of the Win32 threading API as a layer on top of WinRT threadpools.
|
||||
//
|
||||
// Supported features:
|
||||
//
|
||||
// - CreateThread (returns a standard Win32 handle which can be waited on, then closed)
|
||||
// - CREATE_SUSPENDED and ResumeThread
|
||||
// - Partial support for SetThreadPriority (see below)
|
||||
// - Sleep
|
||||
// - Thread local storage (TlsAlloc, TlsFree, TlsGetValue, TlsSetValue)
|
||||
//
|
||||
// Differences from Win32:
|
||||
//
|
||||
// - If using TLS other than from this CreateThread emulation, call TlsShutdown before thread/task exit
|
||||
// - No ExitThread or TerminateThread (just return from the thread function to exit)
|
||||
// - No SuspendThread, so ResumeThread is only useful in combination with CREATE_SUSPENDED
|
||||
// - SetThreadPriority is only available while a thread is in CREATE_SUSPENDED state
|
||||
// - SetThreadPriority only supports three priority levels (negative, zero, or positive)
|
||||
// - No thread identifier APIs (GetThreadId, GetCurrentThreadId, OpenThread)
|
||||
// - No affinity APIs
|
||||
// - No GetExitCodeThread
|
||||
// - Failure cases return error codes but do not always call SetLastError
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace ThreadEmulation
|
||||
{
|
||||
#ifndef CREATE_SUSPENDED
|
||||
#define CREATE_SUSPENDED 0x00000004
|
||||
#endif
|
||||
|
||||
HANDLE WINAPI CreateThread(_In_opt_ LPSECURITY_ATTRIBUTES unusedThreadAttributes, _In_ SIZE_T unusedStackSize, _In_ LPTHREAD_START_ROUTINE lpStartAddress, _In_opt_ LPVOID lpParameter, _In_ DWORD dwCreationFlags, _Out_opt_ LPDWORD unusedThreadId);
|
||||
DWORD WINAPI ResumeThread(_In_ HANDLE hThread);
|
||||
BOOL WINAPI SetThreadPriority(_In_ HANDLE hThread, _In_ int nPriority);
|
||||
|
||||
VOID WINAPI Sleep(_In_ DWORD dwMilliseconds);
|
||||
|
||||
DWORD WINAPI TlsAlloc();
|
||||
BOOL WINAPI TlsFree(_In_ DWORD dwTlsIndex);
|
||||
LPVOID WINAPI TlsGetValue(_In_ DWORD dwTlsIndex);
|
||||
BOOL WINAPI TlsSetValue(_In_ DWORD dwTlsIndex, _In_opt_ LPVOID lpTlsValue);
|
||||
|
||||
void WINAPI TlsShutdown();
|
||||
}
|
||||
Reference in New Issue
Block a user