Version

menu_open

include/AK/Tools/Win32/ThreadEmulation.h

Go to the documentation of this file.
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

Was this page helpful?

Need Support?

Questions? Problems? Need more info? Contact us, and we can help!

Visit our Support page

Tell us about your project. We're here to help.

Register your project and we'll help you get started with no strings attached!

Get started with Wwise