eathread_thread_pc.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include "EABase/eabase.h"
  5. #include "eathread/eathread.h"
  6. #include "eathread/eathread_callstack.h"
  7. #include "eathread/eathread_mutex.h"
  8. #include "eathread/eathread_sync.h"
  9. #include "eathread/eathread_thread.h"
  10. #include "eathread/internal/eathread_global.h"
  11. #if EA_COMPILER_VERSION >= 1900 // VS2015+
  12. // required for windows.h that has mismatch that is included in this file
  13. EA_DISABLE_VC_WARNING(5031 5032)// #pragma warning(pop): likely mismatch, popping warning state pushed in different file / detected #pragma warning(push) with no corresponding
  14. #endif
  15. // Warning 6312 and 6322 are spurious, as we are not execution a case that could possibly loop.
  16. // 6312: Possible infinite loop: use of the constant EXCEPTION_CONTINUE_EXECUTION in the exception-filter expression of a try-except. Execution restarts in the protected block
  17. // 6322: Empty _except block
  18. EA_DISABLE_VC_WARNING(6312 6322)
  19. #if defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE
  20. #include <new>
  21. #include <process.h>
  22. EA_DISABLE_ALL_VC_WARNINGS()
  23. #include <Windows.h>
  24. EA_RESTORE_ALL_VC_WARNINGS()
  25. #if defined(_MSC_VER)
  26. struct ThreadNameInfo{
  27. DWORD dwType;
  28. LPCSTR lpName;
  29. DWORD dwThreadId;
  30. DWORD dwFlags;
  31. };
  32. extern "C" WINBASEAPI DWORD WINAPI SetThreadIdealProcessor(_In_ HANDLE hThread, _In_ DWORD dwIdealProcessor);
  33. extern "C" WINBASEAPI BOOL WINAPI IsDebuggerPresent();
  34. #endif
  35. #ifdef _MSC_VER
  36. #ifndef EATHREAD_INIT_SEG_DEFINED
  37. #define EATHREAD_INIT_SEG_DEFINED
  38. #endif
  39. // We are changing the initalization ordering here because in bulkbuild tool builds the initialization
  40. // order of globals changes and causes a crash when we attempt to lock the Mutex guarding access
  41. // of the EAThreadDynamicData objects. The code attempts to lock a mutex that has been destructed
  42. // and causes a crash within the WindowsSDK. This ensures that global mutex object is not destructed
  43. // until all user code has destructed.
  44. //
  45. #ifndef EATHREAD_INIT_SEG_DEFINED
  46. #define EATHREAD_INIT_SEG_DEFINED
  47. #pragma warning(disable: 4075) // warning C4075: initializers put in unrecognized initialization area
  48. #pragma warning(disable: 4073) //warning C4073: initializers put in library initialization area
  49. #pragma init_seg(lib)
  50. #endif
  51. #endif
  52. namespace EA {
  53. namespace Thread {
  54. extern void SetCurrentThreadHandle(HANDLE hThread, bool bDynamic);
  55. namespace Internal { extern void SetThreadName(EAThreadDynamicData* pTDD, const char* pName); };
  56. }
  57. }
  58. namespace EA
  59. {
  60. namespace Thread
  61. {
  62. extern Allocator* gpAllocator;
  63. static AtomicInt32 nLastProcessor = 0;
  64. const size_t kMaxThreadDynamicDataCount = 128;
  65. struct EAThreadGlobalVars
  66. {
  67. EA_PREFIX_ALIGN(8)
  68. char gThreadDynamicData[kMaxThreadDynamicDataCount][sizeof(EAThreadDynamicData)] EA_POSTFIX_ALIGN(8);
  69. AtomicInt32 gThreadDynamicDataAllocated[kMaxThreadDynamicDataCount];
  70. Mutex gThreadDynamicMutex;
  71. EAThreadGlobalVars() {}
  72. EAThreadGlobalVars(const EAThreadGlobalVars&) {}
  73. EAThreadGlobalVars& operator=(const EAThreadGlobalVars&) {}
  74. };
  75. EATHREAD_GLOBALVARS_CREATE_INSTANCE;
  76. EAThreadDynamicData* AllocateThreadDynamicData()
  77. {
  78. AutoMutex am(EATHREAD_GLOBALVARS.gThreadDynamicMutex);
  79. for(size_t i(0); i < kMaxThreadDynamicDataCount; i++)
  80. {
  81. if(EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[i].SetValueConditional(1, 0))
  82. return (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i];
  83. }
  84. // This is a safety fallback mechanism. In practice it won't be used in almost all situations.
  85. if(gpAllocator)
  86. return (EAThreadDynamicData*)gpAllocator->Alloc(sizeof(EAThreadDynamicData));
  87. else
  88. return new EAThreadDynamicData; // This is a small problem, as this doesn't just allocate it but also constructs it.
  89. }
  90. void FreeThreadDynamicData(EAThreadDynamicData* pEAThreadDynamicData)
  91. {
  92. AutoMutex am(EATHREAD_GLOBALVARS.gThreadDynamicMutex);
  93. if((pEAThreadDynamicData >= (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData) && (pEAThreadDynamicData < ((EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData + kMaxThreadDynamicDataCount)))
  94. {
  95. pEAThreadDynamicData->~EAThreadDynamicData();
  96. EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[pEAThreadDynamicData - (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData].SetValue(0);
  97. }
  98. else
  99. {
  100. // Assume the data was allocated via the fallback mechanism.
  101. if(gpAllocator)
  102. {
  103. pEAThreadDynamicData->~EAThreadDynamicData();
  104. gpAllocator->Free(pEAThreadDynamicData);
  105. }
  106. else
  107. delete pEAThreadDynamicData;
  108. }
  109. }
  110. EAThreadDynamicData* FindThreadDynamicData(ThreadId threadId)
  111. {
  112. for(size_t i(0); i < kMaxThreadDynamicDataCount; i++)
  113. {
  114. EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i];
  115. if(pTDD->mhThread == threadId)
  116. return pTDD;
  117. }
  118. return NULL; // This is no practical way we can find the data unless thread-specific storage was involved.
  119. }
  120. EAThreadDynamicData* FindThreadDynamicData(EA::Thread::SysThreadId sysThreadId)
  121. {
  122. for (size_t i(0); i < kMaxThreadDynamicDataCount; ++i)
  123. {
  124. EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i];
  125. if (pTDD->mnThreadId == sysThreadId)
  126. return pTDD;
  127. }
  128. return nullptr; // This is no practical way we can find the data unless thread-specific storage was involved.
  129. }
  130. bool IsDebuggerPresent()
  131. {
  132. #if defined(EA_PLATFORM_MICROSOFT)
  133. return ::IsDebuggerPresent() != 0;
  134. #else
  135. return false;
  136. #endif
  137. }
  138. }
  139. }
  140. EAThreadDynamicData::EAThreadDynamicData()
  141. : mhThread(EA::Thread::kThreadIdInvalid),
  142. mnThreadId(0), // Note that this is a Windows "thread id", wheras for us thread ids are what Windows calls a thread handle.
  143. mnStatus(EA::Thread::Thread::kStatusNone),
  144. mnReturnValue(0),
  145. mpBeginThreadUserWrapper(NULL),
  146. mnRefCount(0)
  147. {
  148. // Empty
  149. }
  150. EAThreadDynamicData::~EAThreadDynamicData()
  151. {
  152. if(mhThread)
  153. ::CloseHandle(mhThread);
  154. mhThread = EA::Thread::kThreadIdInvalid;
  155. mnThreadId = 0;
  156. }
  157. void EAThreadDynamicData::AddRef()
  158. {
  159. mnRefCount.Increment();
  160. }
  161. void EAThreadDynamicData::Release()
  162. {
  163. if(mnRefCount.Decrement() == 0)
  164. EA::Thread::FreeThreadDynamicData(this);
  165. }
  166. EA::Thread::ThreadParameters::ThreadParameters()
  167. : mpStack(NULL)
  168. , mnStackSize(0)
  169. , mnPriority(kThreadPriorityDefault)
  170. , mnProcessor(kProcessorDefault)
  171. , mnAffinityMask(kThreadAffinityMaskAny)
  172. , mpName("")
  173. , mbDisablePriorityBoost(false)
  174. {
  175. }
  176. EA::Thread::RunnableFunctionUserWrapper EA::Thread::Thread::sGlobalRunnableFunctionUserWrapper = NULL;
  177. EA::Thread::RunnableClassUserWrapper EA::Thread::Thread::sGlobalRunnableClassUserWrapper = NULL;
  178. EA::Thread::AtomicInt32 EA::Thread::Thread::sDefaultProcessor = kProcessorAny;
  179. EA::Thread::AtomicUint64 EA::Thread::Thread::sDefaultProcessorMask = UINT64_C(0xffffffffffffffff);
  180. EA::Thread::RunnableFunctionUserWrapper EA::Thread::Thread::GetGlobalRunnableFunctionUserWrapper()
  181. {
  182. return sGlobalRunnableFunctionUserWrapper;
  183. }
  184. void EA::Thread::Thread::SetGlobalRunnableFunctionUserWrapper(EA::Thread::RunnableFunctionUserWrapper pUserWrapper)
  185. {
  186. if(sGlobalRunnableFunctionUserWrapper != NULL)
  187. {
  188. // Can only be set once in entire game.
  189. EAT_ASSERT(false);
  190. }
  191. else
  192. {
  193. sGlobalRunnableFunctionUserWrapper = pUserWrapper;
  194. }
  195. }
  196. EA::Thread::RunnableClassUserWrapper EA::Thread::Thread::GetGlobalRunnableClassUserWrapper()
  197. {
  198. return sGlobalRunnableClassUserWrapper;
  199. }
  200. void EA::Thread::Thread::SetGlobalRunnableClassUserWrapper(EA::Thread::RunnableClassUserWrapper pUserWrapper)
  201. {
  202. if(sGlobalRunnableClassUserWrapper != NULL)
  203. {
  204. // Can only be set once.
  205. EAT_ASSERT(false);
  206. }
  207. else
  208. sGlobalRunnableClassUserWrapper = pUserWrapper;
  209. }
  210. // Helper that selects a target processor based on the provided ThreadParameters structure and the various
  211. // pieces of shared state that EAThread maintains to implement a 'round-robin' style processor selection.
  212. int SelectProcessor(const EA::Thread::ThreadParameters* pTP, EA::Thread::AtomicInt32& sDefaultProcessor, EA::Thread::AtomicUint64& sDefaultProcessorMask)
  213. {
  214. int nProcessor;
  215. if (pTP && (pTP->mnProcessor >= 0))
  216. {
  217. nProcessor = pTP->mnProcessor;
  218. // This is a small attempt to try to spread out threads between processors. We don't
  219. // care much if another thread happens to be created here and races with this.
  220. if (nProcessor == EA::Thread::nLastProcessor)
  221. ++EA::Thread::nLastProcessor;
  222. }
  223. else
  224. {
  225. #if defined(EA_PLATFORM_MICROSOFT)
  226. if (!pTP || pTP->mnProcessor == EA::Thread::kProcessorAny)
  227. {
  228. // If the processor is not specified, then allow the scheduler to
  229. // run the thread on any available processor
  230. nProcessor = EA::Thread::kProcessorDefault;
  231. }
  232. else
  233. #endif
  234. if (sDefaultProcessor >= 0) // If the user has identified a specific processor...
  235. nProcessor = sDefaultProcessor;
  236. else if(sDefaultProcessor == EA::Thread::kProcessorDefault) // If the user explicitly asked for the default processor
  237. nProcessor = sDefaultProcessor;
  238. else
  239. {
  240. // NOTE(rparolin): The reason we have this round-robin code is that the
  241. // originally we used it on Xenon OS which required us to pick a CPU to run on.
  242. // After the Xenon was deprecated this code remained and is now a functional
  243. // requirement. We should probably deprecate and remove in the future but
  244. // currently teams are dependent on it.
  245. const uint64_t processorMask = sDefaultProcessorMask.GetValue();
  246. do
  247. {
  248. nProcessor = EA::Thread::nLastProcessor.Increment();
  249. if (nProcessor == MAXIMUM_PROCESSORS)
  250. {
  251. EA::Thread::nLastProcessor.SetValueConditional(0, MAXIMUM_PROCESSORS);
  252. nProcessor = 0;
  253. }
  254. } while ((((uint64_t)1 << nProcessor) & processorMask) == 0);
  255. }
  256. }
  257. return nProcessor;
  258. }
  259. EA::Thread::Thread::Thread()
  260. {
  261. mThreadData.mpData = NULL;
  262. }
  263. EA::Thread::Thread::Thread(const Thread& t)
  264. : mThreadData(t.mThreadData)
  265. {
  266. if(mThreadData.mpData)
  267. mThreadData.mpData->AddRef();
  268. }
  269. EA::Thread::Thread& EA::Thread::Thread::operator=(const Thread& t)
  270. {
  271. // We don't synchronize access to mpData; we assume that the user
  272. // synchronizes it or this Thread instances is used from a single thread.
  273. if(t.mThreadData.mpData)
  274. t.mThreadData.mpData->AddRef();
  275. if(mThreadData.mpData)
  276. mThreadData.mpData->Release();
  277. mThreadData = t.mThreadData;
  278. return *this;
  279. }
  280. EA::Thread::Thread::~Thread()
  281. {
  282. // We don't synchronize access to mpData; we assume that the user
  283. // synchronizes it or this Thread instances is used from a single thread.
  284. if(mThreadData.mpData)
  285. mThreadData.mpData->Release();
  286. }
  287. #if defined(EA_PLATFORM_XBOXONE)
  288. static DWORD WINAPI RunnableFunctionInternal(void* pContext)
  289. #else
  290. static unsigned int __stdcall RunnableFunctionInternal(void* pContext)
  291. #endif
  292. {
  293. // The parent thread is sharing memory with us and we need to
  294. // make sure our view of it is synchronized with the parent.
  295. EAReadWriteBarrier();
  296. EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)pContext;
  297. EA::Thread::RunnableFunction pFunction = (EA::Thread::RunnableFunction)pTDD->mpStartContext[0];
  298. void* pCallContext = pTDD->mpStartContext[1];
  299. EA::Thread::SetCurrentThreadHandle(pTDD->mpStartContext[2], false);
  300. pTDD->mpStackBase = EA::Thread::GetStackBase();
  301. pTDD->mnStatus = EA::Thread::Thread::kStatusRunning;
  302. EA::Thread::SetThreadName(pTDD->mhThread, pTDD->mName);
  303. if(pTDD->mpBeginThreadUserWrapper != NULL)
  304. {
  305. EA::Thread::RunnableFunctionUserWrapper pWrapperFunction = (EA::Thread::RunnableFunctionUserWrapper)pTDD->mpBeginThreadUserWrapper;
  306. // if user wrapper is specified, call user wrapper and pass down the pFunction and pContext
  307. pTDD->mnReturnValue = pWrapperFunction(pFunction, pCallContext);
  308. }
  309. else
  310. {
  311. pTDD->mnReturnValue = pFunction(pCallContext);
  312. }
  313. const unsigned int nReturnValue = (unsigned int)pTDD->mnReturnValue;
  314. EA::Thread::SetCurrentThreadHandle(0, false);
  315. pTDD->mnStatus = EA::Thread::Thread::kStatusEnded;
  316. pTDD->Release();
  317. return nReturnValue;
  318. }
  319. void EA::Thread::Thread::SetAffinityMask(EA::Thread::ThreadAffinityMask nAffinityMask)
  320. {
  321. if(mThreadData.mpData->mhThread)
  322. {
  323. EA::Thread::SetThreadAffinityMask(mThreadData.mpData->mhThread, nAffinityMask);
  324. }
  325. }
  326. EA::Thread::ThreadAffinityMask EA::Thread::Thread::GetAffinityMask()
  327. {
  328. if(mThreadData.mpData->mhThread)
  329. {
  330. return mThreadData.mpData->mnThreadAffinityMask;
  331. }
  332. return kThreadAffinityMaskAny;
  333. }
  334. EA::Thread::ThreadId EA::Thread::Thread::Begin(RunnableFunction pFunction, void* pContext, const ThreadParameters* pTP, RunnableFunctionUserWrapper pUserWrapper)
  335. {
  336. // Check there is an entry for the current thread context in our ThreadDynamicData array.
  337. ThreadId thisThreadId = GetThreadId();
  338. if(!FindThreadDynamicData(thisThreadId))
  339. {
  340. EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData;
  341. if(pData)
  342. {
  343. pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread.
  344. // Do no AddRef for thread execution because this is not an EAThread managed thread.
  345. pData->AddRef(); // AddRef for this function, to be released upon this function's exit.
  346. pData->mhThread = thisThreadId;
  347. pData->mnThreadId = GetCurrentThreadId();
  348. strncpy(pData->mName, "external", EATHREAD_NAME_SIZE);
  349. pData->mName[EATHREAD_NAME_SIZE - 1] = 0;
  350. pData->mpStackBase = EA::Thread::GetStackBase();
  351. }
  352. }
  353. if(mThreadData.mpData)
  354. mThreadData.mpData->Release(); // Matches the "AddRef for ourselves" below.
  355. // Win32-like platforms don't support user-supplied stacks. A user-supplied stack pointer
  356. // here would be a waste of user memory, and so we assert that mpStack == NULL.
  357. EAT_ASSERT(!pTP || (pTP->mpStack == NULL));
  358. // We use the pData temporary throughout this function because it's possible that mThreadData.mpData could be
  359. // modified as we are executing, in particular in the case that mThreadData.mpData is destroyed and changed
  360. // during execution.
  361. EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData; // Note that we use a special new here which doesn't use the heap.
  362. mThreadData.mpData = pData;
  363. pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread.
  364. pData->AddRef(); // AddRef for the thread, to be released upon the thread exiting.
  365. pData->AddRef(); // AddRef for this function, to be released upon this function's exit.
  366. pData->mhThread = kThreadIdInvalid;
  367. pData->mpStartContext[0] = pFunction;
  368. pData->mpStartContext[1] = pContext;
  369. pData->mpBeginThreadUserWrapper = pUserWrapper;
  370. pData->mnThreadAffinityMask = pTP ? pTP->mnAffinityMask : kThreadAffinityMaskAny;
  371. const unsigned nStackSize = pTP ? (unsigned)pTP->mnStackSize : 0;
  372. #if defined(EA_PLATFORM_XBOXONE)
  373. // Capilano no longer supports _beginthreadex. Using CreateThread instead may cause issues when using the MS CRT
  374. // according to MSDN (memory leaks or possibly crashes) as it does not initialize the CRT. This a reasonable
  375. // workaround while we wait for clarification from MS on what the recommended threading APIs are for Capilano.
  376. HANDLE hThread = CreateThread(NULL, nStackSize, RunnableFunctionInternal, pData, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&pData->mnThreadId));
  377. #else
  378. HANDLE hThread = (HANDLE)_beginthreadex(NULL, nStackSize, RunnableFunctionInternal, pData, CREATE_SUSPENDED, &pData->mnThreadId);
  379. #endif
  380. if(hThread)
  381. {
  382. pData->mhThread = hThread;
  383. if(pTP)
  384. SetName(pTP->mpName);
  385. pData->mpStartContext[2] = hThread;
  386. if(pTP && (pTP->mnPriority != kThreadPriorityDefault))
  387. SetPriority(pTP->mnPriority);
  388. #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE)
  389. if (pTP)
  390. {
  391. auto result = SetThreadPriorityBoost(pData->mhThread, pTP->mbDisablePriorityBoost);
  392. EAT_ASSERT(result != 0);
  393. EA_UNUSED(result);
  394. }
  395. #endif
  396. #if defined(EA_PLATFORM_MICROSOFT)
  397. int nProcessor = SelectProcessor(pTP, sDefaultProcessor, sDefaultProcessorMask);
  398. if(pTP && pTP->mnProcessor == EA::Thread::kProcessorAny)
  399. SetAffinityMask(pTP->mnAffinityMask);
  400. else
  401. SetProcessor(nProcessor);
  402. #endif
  403. ResumeThread(hThread);
  404. pData->Release(); // Matches AddRef for this function.
  405. return hThread;
  406. }
  407. pData->Release(); // Matches AddRef for this function.
  408. pData->Release(); // Matches AddRef for this Thread class above.
  409. pData->Release(); // Matches AddRef for the thread above.
  410. mThreadData.mpData = NULL; // mThreadData.mpData == pData
  411. return (ThreadId)kThreadIdInvalid;
  412. }
  413. #if defined(EA_PLATFORM_XBOXONE)
  414. static DWORD WINAPI RunnableObjectInternal(void* pContext)
  415. #else
  416. static unsigned int __stdcall RunnableObjectInternal(void* pContext)
  417. #endif
  418. {
  419. // The parent thread is sharing memory with us and we need to
  420. // make sure our view of it is synchronized with the parent.
  421. EAReadWriteBarrier();
  422. EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)pContext;
  423. EA::Thread::IRunnable* pRunnable = (EA::Thread::IRunnable*)pTDD->mpStartContext[0];
  424. void* pCallContext = pTDD->mpStartContext[1];
  425. EA::Thread::SetCurrentThreadHandle(pTDD->mpStartContext[2], false);
  426. pTDD->mnStatus = EA::Thread::Thread::kStatusRunning;
  427. EA::Thread::SetThreadName(pTDD->mhThread, pTDD->mName);
  428. if(pTDD->mpBeginThreadUserWrapper)
  429. {
  430. EA::Thread::RunnableClassUserWrapper pWrapperClass = (EA::Thread::RunnableClassUserWrapper)pTDD->mpBeginThreadUserWrapper;
  431. // if user wrapper is specified, call user wrapper and pass down the pFunction and pContext
  432. pTDD->mnReturnValue = pWrapperClass(pRunnable, pCallContext);
  433. }
  434. else
  435. pTDD->mnReturnValue = pRunnable->Run(pCallContext);
  436. const unsigned int nReturnValue = (unsigned int)pTDD->mnReturnValue;
  437. EA::Thread::SetCurrentThreadHandle(0, false);
  438. pTDD->mnStatus = EA::Thread::Thread::kStatusEnded;
  439. pTDD->Release();
  440. return nReturnValue;
  441. }
  442. EA::Thread::ThreadId EA::Thread::Thread::Begin(IRunnable* pRunnable, void* pContext, const ThreadParameters* pTP, RunnableClassUserWrapper pUserWrapper)
  443. {
  444. if(mThreadData.mpData)
  445. mThreadData.mpData->Release(); // Matches the "AddRef for ourselves" below.
  446. // Win32-like platforms don't support user-supplied stacks. A user-supplied stack pointer
  447. // here would be a waste of user memory, and so we assert that mpStack == NULL.
  448. EAT_ASSERT(!pTP || (pTP->mpStack == NULL));
  449. // We use the pData temporary throughout this function because it's possible that mThreadData.mpData could be
  450. // modified as we are executing, in particular in the case that mThreadData.mpData is destroyed and changed
  451. // during execution.
  452. EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData; // Note that we use a special new here which doesn't use the heap.
  453. mThreadData.mpData = pData;
  454. pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread.
  455. pData->AddRef(); // AddRef for the thread, to be released upon the thread exiting.
  456. pData->AddRef(); // AddRef for this function, to be released upon this function's exit.
  457. pData->mhThread = kThreadIdInvalid;
  458. pData->mpStartContext[0] = pRunnable;
  459. pData->mpStartContext[1] = pContext;
  460. pData->mpBeginThreadUserWrapper = pUserWrapper;
  461. pData->mnThreadAffinityMask = pTP ? pTP->mnAffinityMask : kThreadAffinityMaskAny;
  462. const unsigned nStackSize = pTP ? (unsigned)pTP->mnStackSize : 0;
  463. #if defined(EA_PLATFORM_XBOXONE)
  464. // Capilano no longer supports _beginthreadex. Using CreateThread instead may cause issues when using the MS CRT
  465. // according to MSDN (memory leaks or possibly crashes) as it does not initialize the CRT. This a reasonable
  466. // workaround while we wait for clarification from MS on what the recommended threading APIs are for Capilano.
  467. HANDLE hThread = CreateThread(NULL, nStackSize, RunnableObjectInternal, pData, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&pData->mnThreadId));
  468. #else
  469. HANDLE hThread = (HANDLE)_beginthreadex(NULL, nStackSize, RunnableObjectInternal, pData, CREATE_SUSPENDED, &pData->mnThreadId);
  470. #endif
  471. if(hThread)
  472. {
  473. pData->mhThread = hThread;
  474. if(pTP)
  475. SetName(pTP->mpName);
  476. pData->mpStartContext[2] = hThread;
  477. if(pTP && (pTP->mnPriority != kThreadPriorityDefault))
  478. SetPriority(pTP->mnPriority);
  479. #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE)
  480. if (pTP)
  481. {
  482. auto result = SetThreadPriorityBoost(pData->mhThread, pTP->mbDisablePriorityBoost);
  483. EAT_ASSERT(result != 0);
  484. EA_UNUSED(result);
  485. }
  486. #endif
  487. #if defined(EA_PLATFORM_MICROSOFT)
  488. int nProcessor = SelectProcessor(pTP, sDefaultProcessor, sDefaultProcessorMask);
  489. if(pTP && pTP->mnProcessor == EA::Thread::kProcessorAny)
  490. SetAffinityMask(pTP->mnAffinityMask);
  491. else
  492. SetProcessor(nProcessor);
  493. #endif
  494. ResumeThread(hThread); // This will unsuspend the thread.
  495. pData->Release(); // Matches AddRef for this function.
  496. return hThread;
  497. }
  498. pData->Release(); // Matches AddRef for this function.
  499. pData->Release(); // Matches AddRef for this Thread class above.
  500. pData->Release(); // Matches AddRef for the thread above.
  501. mThreadData.mpData = NULL;
  502. return (ThreadId)kThreadIdInvalid;
  503. }
  504. EA::Thread::Thread::Status EA::Thread::Thread::WaitForEnd(const ThreadTime& timeoutAbsolute, intptr_t* pThreadReturnValue)
  505. {
  506. // The mThreadData memory is shared between threads and when
  507. // reading it we must be synchronized.
  508. EAReadWriteBarrier();
  509. // A mutex lock around mpData is not needed below because
  510. // mpData is never allowed to go from non-NULL to NULL.
  511. // Todo: Consider that there may be a subtle race condition here if
  512. // the user immediately calls WaitForEnd right after calling Begin.
  513. if(mThreadData.mpData)
  514. {
  515. if(mThreadData.mpData->mhThread) // If it was started...
  516. {
  517. // We must not call WaitForEnd from the thread we are waiting to end. That would result in a deadlock.
  518. EAT_ASSERT(mThreadData.mpData->mhThread != EA::Thread::GetThreadId());
  519. // dwResult normally should be 'WAIT_OBJECT_0', but can also be WAIT_ABANDONED or WAIT_FAILED.
  520. const DWORD dwResult = ::WaitForSingleObject(mThreadData.mpData->mhThread, RelativeTimeoutFromAbsoluteTimeout(timeoutAbsolute));
  521. if(dwResult == WAIT_TIMEOUT)
  522. return kStatusRunning;
  523. // Close the handle now so as to minimize handle proliferation.
  524. ::CloseHandle(mThreadData.mpData->mhThread);
  525. mThreadData.mpData->mhThread = 0;
  526. mThreadData.mpData->mnStatus = kStatusEnded;
  527. }
  528. if(pThreadReturnValue)
  529. {
  530. EAReadWriteBarrier();
  531. *pThreadReturnValue = mThreadData.mpData->mnReturnValue;
  532. }
  533. return kStatusEnded; // A thread was created, so it must have ended.
  534. }
  535. else
  536. {
  537. // Else the user hasn't started the thread yet, so we wait until the user starts it.
  538. // Ideally, what we really want to do here is wait for some kind of signal.
  539. // Instead for the time being we do a polling loop.
  540. while((!mThreadData.mpData || !mThreadData.mpData->mhThread) && (GetThreadTime() < timeoutAbsolute))
  541. {
  542. ThreadSleep(1);
  543. EAReadWriteBarrier();
  544. EACompilerMemoryBarrier();
  545. }
  546. if(mThreadData.mpData)
  547. return WaitForEnd(timeoutAbsolute);
  548. }
  549. return kStatusNone; // No thread has been started.
  550. }
  551. EA::Thread::Thread::Status EA::Thread::Thread::GetStatus(intptr_t* pThreadReturnValue) const
  552. {
  553. // The mThreadData memory is shared between threads and when
  554. // reading it we must be synchronized.
  555. EAReadWriteBarrier();
  556. // A mutex lock around mpData is not needed below because
  557. // mpData is never allowed to go from non-NULL to NULL.
  558. if(mThreadData.mpData)
  559. {
  560. if(mThreadData.mpData->mhThread) // If the thread has been started...
  561. {
  562. DWORD dwExitStatus;
  563. // Note that GetExitCodeThread is a hazard if the user of a thread exits
  564. // with a return value that is equal to the value of STILL_ACTIVE (i.e. 259).
  565. // We can document that users shouldn't do this, or we can change the code
  566. // here to use WaitForSingleObject(hThread, 0) and assume the thread is
  567. // still active if the return value is WAIT_TIMEOUT.
  568. if(::GetExitCodeThread(mThreadData.mpData->mhThread, &dwExitStatus))
  569. {
  570. if(dwExitStatus == STILL_ACTIVE)
  571. return kStatusRunning; // Nothing has changed.
  572. ::CloseHandle(mThreadData.mpData->mhThread); // Do this now so as to minimize handle proliferation.
  573. mThreadData.mpData->mhThread = 0;
  574. } // else fall through.
  575. } // else fall through.
  576. if(pThreadReturnValue)
  577. *pThreadReturnValue = mThreadData.mpData->mnReturnValue;
  578. mThreadData.mpData->mnStatus = kStatusEnded;
  579. return kStatusEnded;
  580. }
  581. return kStatusNone;
  582. }
  583. EA::Thread::ThreadId EA::Thread::Thread::GetId() const
  584. {
  585. // A mutex lock around mpData is not needed below because
  586. // mpData is never allowed to go from non-NULL to NULL.
  587. if(mThreadData.mpData)
  588. return (ThreadId)mThreadData.mpData->mhThread;
  589. return kThreadIdInvalid;
  590. }
  591. int EA::Thread::Thread::GetPriority() const
  592. {
  593. // A mutex lock around mpData is not needed below because
  594. // mpData is never allowed to go from non-NULL to NULL.
  595. if(mThreadData.mpData)
  596. {
  597. const int nPriority = ::GetThreadPriority(mThreadData.mpData->mhThread);
  598. return kThreadPriorityDefault + (nPriority - THREAD_PRIORITY_NORMAL);
  599. }
  600. return kThreadPriorityUnknown;
  601. }
  602. bool EA::Thread::Thread::SetPriority(int nPriority)
  603. {
  604. // A mutex lock around mpData is not needed below because
  605. // mpData is never allowed to go from non-NULL to NULL.
  606. // For more information on how Windows handle thread priority based on process priority, see
  607. // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/scheduling_priorities.asp
  608. EAT_ASSERT(nPriority != kThreadPriorityUnknown);
  609. if(mThreadData.mpData)
  610. {
  611. int nNewPriority = THREAD_PRIORITY_NORMAL + (nPriority - kThreadPriorityDefault);
  612. bool result = ::SetThreadPriority(mThreadData.mpData->mhThread, nNewPriority) != 0;
  613. // Windows process running in NORMAL_PRIORITY_CLASS is picky about the priority passed in.
  614. // So we need to set the priority to the next priority supported
  615. #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE)
  616. while(!result)
  617. {
  618. if(nNewPriority >= THREAD_PRIORITY_TIME_CRITICAL)
  619. return ::SetThreadPriority(mThreadData.mpData->mhThread, THREAD_PRIORITY_TIME_CRITICAL) != 0;
  620. if(nNewPriority <= THREAD_PRIORITY_IDLE)
  621. return ::SetThreadPriority(mThreadData.mpData->mhThread, THREAD_PRIORITY_IDLE) != 0;
  622. result = ::SetThreadPriority(mThreadData.mpData->mhThread, nNewPriority) != 0;
  623. nNewPriority++;
  624. }
  625. #endif
  626. return result;
  627. }
  628. return false;
  629. }
  630. void EA::Thread::Thread::SetProcessor(int nProcessor)
  631. {
  632. if(mThreadData.mpData)
  633. {
  634. #if defined(EA_PLATFORM_XBOXONE)
  635. static int nProcessorCount = GetProcessorCount();
  636. if(nProcessor >= nProcessorCount)
  637. nProcessor %= nProcessorCount;
  638. ThreadAffinityMask mask = 0x7F; // default to all 7 available cores.
  639. if (nProcessor >= 0)
  640. mask = ((ThreadAffinityMask)1) << nProcessor;
  641. SetThreadAffinityMask(mThreadData.mpData->mhThread, mask);
  642. #else
  643. static int nProcessorCount = GetProcessorCount();
  644. if(nProcessor < 0)
  645. nProcessor = MAXIMUM_PROCESSORS; // This causes the SetThreadIdealProcessor to reset to 'no ideal processor'.
  646. else
  647. {
  648. if(nProcessor >= nProcessorCount)
  649. nProcessor %= nProcessorCount;
  650. }
  651. // SetThreadIdealProcessor differs from SetThreadAffinityMask in that SetThreadIdealProcessor is not
  652. // a strict assignment, and it allows the OS to move the thread if the ideal processor is busy.
  653. // SetThreadAffinityMask is a more rigid assignment, but it can result in slower performance and
  654. // possibly hangs due to processor contention between threads. For Windows we use SetIdealThreadProcessor
  655. // in the name of safety and likely better overall performance.
  656. SetThreadIdealProcessor(mThreadData.mpData->mhThread, (DWORD)nProcessor);
  657. #endif
  658. }
  659. }
  660. typedef VOID (APIENTRY *PAPCFUNC)(_In_ ULONG_PTR dwParam);
  661. extern "C" WINBASEAPI DWORD WINAPI QueueUserAPC(_In_ PAPCFUNC pfnAPC, _In_ HANDLE hThread, _In_ ULONG_PTR dwData);
  662. void EA::Thread::Thread::Wake()
  663. {
  664. // A mutex lock around mpData is not needed below because
  665. // mpData is never allowed to go from non-NULL to NULL.
  666. struct ThreadWake{ static void WINAPI Empty(ULONG_PTR){} };
  667. if(mThreadData.mpData && mThreadData.mpData->mhThread)
  668. ::QueueUserAPC((PAPCFUNC)ThreadWake::Empty, mThreadData.mpData->mhThread, 0);
  669. }
  670. const char* EA::Thread::Thread::GetName() const
  671. {
  672. if(mThreadData.mpData)
  673. return mThreadData.mpData->mName;
  674. return "";
  675. }
  676. void EA::Thread::Thread::SetName(const char* pName)
  677. {
  678. if (mThreadData.mpData && pName)
  679. EA::Thread::Internal::SetThreadName(mThreadData.mpData, pName);
  680. }
  681. #endif // EA_PLATFORM_MICROSOFT
  682. EA_RESTORE_VC_WARNING()
  683. #if EA_COMPILER_VERSION >= 1900 // VS2015+
  684. EA_RESTORE_VC_WARNING()// #pragma warning(pop): likely mismatch, popping warning state pushed in different file / detected #pragma warning(push) with no corresponding
  685. #endif