Version

menu_open

include/AK/Tools/Win32/ThreadEmulation.h

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

Cette page a-t-elle été utile ?

Besoin d'aide ?

Des questions ? Des problèmes ? Besoin de plus d'informations ? Contactez-nous, nous pouvons vous aider !

Visitez notre page d'Aide

Décrivez-nous de votre projet. Nous sommes là pour vous aider.

Enregistrez votre projet et nous vous aiderons à démarrer sans aucune obligation !

Partir du bon pied avec Wwise