00001 00002 // 00003 // Copyright (c) 2006 Audiokinetic Inc. / All Rights Reserved 00004 // 00006 00007 /* 00008 * Emulates a subset of the Win32 threading API as a layer on top of WinRT threadpools. 00009 * 00010 * Supported features: 00011 * 00012 * - CreateThread (returns a standard Win32 handle which can be waited on, then closed) 00013 * - CREATE_SUSPENDED and ResumeThread 00014 * - Partial support for SetThreadPriority (see below) 00015 * - Sleep 00016 * - Thread local storage (TlsAlloc, TlsFree, TlsGetValue, TlsSetValue) 00017 * 00018 * Differences from Win32: 00019 * 00020 * - No ExitThread or TerminateThread (just return from the thread function to exit) 00021 * - No SuspendThread, so ResumeThread is only useful in combination with CREATE_SUSPENDED 00022 * - SetThreadPriority is only available while a thread is in CREATE_SUSPENDED state 00023 * - SetThreadPriority only supports three priority levels (negative, zero, or positive) 00024 * - TLS requires use of this CreateThread (leaks memory if called from other threadpool tasks) 00025 * - No thread identifier APIs (GetThreadId, GetCurrentThreadId, OpenThread) 00026 * - No affinity APIs 00027 * - No GetExitCodeThread 00028 * - Failure cases return error codes but do not always call SetLastError 00029 */ 00030 00031 #pragma once 00032 00033 namespace AK 00034 { 00035 namespace ThreadEmulation 00036 { 00037 HANDLE WINAPI CreateThread(__in LPTHREAD_START_ROUTINE lpStartAddress, __in_opt LPVOID lpParameter, __in DWORD dwCreationFlags); 00038 DWORD WINAPI ResumeThread(__in HANDLE hThread); 00039 BOOL WINAPI SetThreadPriority(__in HANDLE hThread, __in int nPriority); 00040 00041 VOID WINAPI Sleep(__in DWORD dwMilliseconds); 00042 VOID WINAPI SleepEx(__in DWORD dwMilliseconds, BOOL in_bAlertable ); 00043 } 00044 } 00045 00046 #ifdef AK_IMPLEMENT_THREAD_EMULATION 00047 00048 #include <assert.h> 00049 #include <vector> 00050 #include <set> 00051 #include <map> 00052 #include <mutex> 00053 00054 namespace AK 00055 { 00056 namespace ThreadEmulation 00057 { 00058 // Stored data for CREATE_SUSPENDED and ResumeThread. 00059 struct PendingThreadInfo 00060 { 00061 LPTHREAD_START_ROUTINE lpStartAddress; 00062 LPVOID lpParameter; 00063 HANDLE completionEvent; 00064 int nPriority; 00065 }; 00066 00067 static std::map<HANDLE, PendingThreadInfo> pendingThreads; 00068 static std::mutex pendingThreadsLock; 00069 00070 00071 // Converts a Win32 thread priority to WinRT format. 00072 static Windows::System::Threading::WorkItemPriority GetWorkItemPriority(int nPriority) 00073 { 00074 if (nPriority < 0) 00075 return Windows::System::Threading::WorkItemPriority::Low; 00076 else if (nPriority > 0) 00077 return Windows::System::Threading::WorkItemPriority::High; 00078 else 00079 return Windows::System::Threading::WorkItemPriority::Normal; 00080 } 00081 00082 00083 // Helper shared between CreateThread and ResumeThread. 00084 static void StartThread(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, HANDLE completionEvent, int nPriority) 00085 { 00086 auto workItemHandler = ref new Windows::System::Threading::WorkItemHandler([=](Windows::Foundation::IAsyncAction^) 00087 { 00088 // Run the user callback. 00089 lpStartAddress(lpParameter); 00090 00091 // Signal that the thread has completed. 00092 SetEvent(completionEvent); 00093 CloseHandle(completionEvent); 00094 00095 }, Platform::CallbackContext::Any); 00096 00097 // Start the threadpool task. 00098 auto workItem = Windows::System::Threading::ThreadPool::RunAsync(workItemHandler, GetWorkItemPriority(nPriority), Windows::System::Threading::WorkItemOptions::TimeSliced ); 00099 00100 // workItem->Start(); 00101 } 00102 00103 00104 HANDLE WINAPI CreateThread(__in LPTHREAD_START_ROUTINE lpStartAddress, __in_opt LPVOID lpParameter, __in DWORD dwCreationFlags) 00105 { 00106 // Validate parameters. 00107 assert((dwCreationFlags & ~CREATE_SUSPENDED) == 0); 00108 00109 // Create a handle that will be signalled when the thread has completed. 00110 HANDLE threadHandle = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, STANDARD_RIGHTS_ALL|EVENT_MODIFY_STATE); 00111 00112 if (!threadHandle) 00113 return nullptr; 00114 00115 // Make a copy of the handle for internal use. This is necessary because 00116 // the caller is responsible for closing the handle returned by CreateThread, 00117 // and they may do that before or after the thread has finished running. 00118 HANDLE completionEvent; 00119 00120 if (!DuplicateHandle(GetCurrentProcess(), threadHandle, GetCurrentProcess(), &completionEvent, 0, false, DUPLICATE_SAME_ACCESS)) 00121 { 00122 CloseHandle(threadHandle); 00123 return nullptr; 00124 } 00125 00126 try 00127 { 00128 if (dwCreationFlags & CREATE_SUSPENDED) 00129 { 00130 // Store info about a suspended thread. 00131 PendingThreadInfo info; 00132 00133 info.lpStartAddress = lpStartAddress; 00134 info.lpParameter = lpParameter; 00135 info.completionEvent = completionEvent; 00136 info.nPriority = 0; 00137 00138 std::lock_guard<std::mutex> lock(pendingThreadsLock); 00139 00140 pendingThreads[threadHandle] = info; 00141 } 00142 else 00143 { 00144 // Start the thread immediately. 00145 StartThread(lpStartAddress, lpParameter, completionEvent, 0); 00146 } 00147 00148 return threadHandle; 00149 } 00150 catch (...) 00151 { 00152 // Clean up if thread creation fails. 00153 CloseHandle(threadHandle); 00154 CloseHandle(completionEvent); 00155 00156 return nullptr; 00157 } 00158 } 00159 00160 00161 DWORD WINAPI ResumeThread(__in HANDLE hThread) 00162 { 00163 std::lock_guard<std::mutex> lock(pendingThreadsLock); 00164 00165 // Look up the requested thread. 00166 auto threadInfo = pendingThreads.find(hThread); 00167 00168 if (threadInfo == pendingThreads.end()) 00169 { 00170 // Can only resume threads while they are in CREATE_SUSPENDED state. 00171 assert(false); 00172 return (DWORD)-1; 00173 } 00174 00175 PendingThreadInfo& info = threadInfo->second; 00176 00177 // Start the thread. 00178 try 00179 { 00180 StartThread(info.lpStartAddress, info.lpParameter, info.completionEvent, info.nPriority); 00181 } 00182 catch (...) 00183 { 00184 return (DWORD)-1; 00185 } 00186 00187 // Remove this thread from the pending list. 00188 pendingThreads.erase(threadInfo); 00189 00190 return 0; 00191 } 00192 00193 00194 BOOL WINAPI SetThreadPriority(__in HANDLE hThread, __in int nPriority) 00195 { 00196 std::lock_guard<std::mutex> lock(pendingThreadsLock); 00197 00198 // Look up the requested thread. 00199 auto threadInfo = pendingThreads.find(hThread); 00200 00201 if (threadInfo == pendingThreads.end()) 00202 { 00203 // Can only set priority on threads while they are in CREATE_SUSPENDED state. 00204 assert(false); 00205 return false; 00206 } 00207 00208 // Store the new priority. 00209 threadInfo->second.nPriority = nPriority; 00210 00211 return true; 00212 } 00213 00214 VOID WINAPI Sleep(__in DWORD dwMilliseconds ) 00215 { 00216 SleepEx( dwMilliseconds, FALSE ); 00217 } 00218 00219 VOID WINAPI SleepEx(__in DWORD dwMilliseconds, BOOL in_bAlertable) 00220 { 00221 static HANDLE singletonEvent = nullptr; 00222 00223 HANDLE sleepEvent = singletonEvent; 00224 00225 // Demand create the event. 00226 if (!sleepEvent) 00227 { 00228 sleepEvent = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, STANDARD_RIGHTS_ALL|EVENT_MODIFY_STATE); 00229 00230 if (!sleepEvent) 00231 return; 00232 00233 HANDLE previousEvent = InterlockedCompareExchangePointerRelease(&singletonEvent, sleepEvent, nullptr); 00234 00235 if (previousEvent) 00236 { 00237 // Back out if multiple threads try to demand create at the same time. 00238 CloseHandle(sleepEvent); 00239 sleepEvent = previousEvent; 00240 } 00241 } 00242 00243 // Emulate sleep by waiting with timeout on an event that is never signalled. 00244 DWORD dwResult = WaitForSingleObjectEx(sleepEvent, dwMilliseconds, in_bAlertable); 00245 assert( dwResult != -1 ); 00246 } 00247 } 00248 } 00249 00250 #endif
Questions? Problems? Need more info? Contact us, and we can help!
Visit our Support pageRegister your project and we'll help you get started with no strings attached!
Get started with Wwise