目录

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