eathread_pc.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  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_thread.h"
  7. #include "eathread/eathread_storage.h"
  8. #if defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE
  9. #include <process.h>
  10. EA_DISABLE_ALL_VC_WARNINGS()
  11. #include <Windows.h>
  12. #include <stdlib.h> // for mbstowcs
  13. #include <setjmp.h>
  14. EA_RESTORE_ALL_VC_WARNINGS()
  15. #include "eathread/eathread_futex.h"
  16. extern "C" WINBASEAPI DWORD WINAPI SetThreadIdealProcessor(_In_ HANDLE hThread, _In_ DWORD dwIdealProcessor);
  17. #if defined(EA_PLATFORM_WIN64)
  18. extern "C" WINBASEAPI DWORD WINAPI GetThreadId(_In_ HANDLE hThread);
  19. extern "C" WINBASEAPI ULONGLONG GetTickCount64(VOID); // Will not run on pre-Vista OS so 64 bit XP not supported
  20. #endif
  21. // We set this module to initialize early. We want to do this because it
  22. // allows statically initialized objects to call these functions safely.
  23. EA_DISABLE_VC_WARNING(4074) // warning C4074: initializers put in compiler reserved initialization area
  24. #pragma init_seg(compiler)
  25. EA_RESTORE_VC_WARNING()
  26. #ifndef EATHREAD_INIT_SEG_DEFINED
  27. #define EATHREAD_INIT_SEG_DEFINED
  28. #endif
  29. namespace EA
  30. {
  31. namespace Thread
  32. {
  33. // Note by Paul Pedriana:
  34. // There is a bit of code here which implements "dynamic thread array maintenance".
  35. // The reason for this is that we are trying to present to the user a consistently
  36. // behaving GetThreadId function. The Windows threading API has a number of design
  37. // characteristics that make it less than ideal for applications. One of these
  38. // designs is that an application cannot ask the system what its thread id is and
  39. // get a consistent answer; in fact you always get a different answer.
  40. // To consider: Use the VC++ undocumented __tlregdtor function to detect thread exits.
  41. // __tlregdtor is a VC++ CRT function which detects the exiting of any threads created
  42. // with the CRT beginthread family of functions. It cannot detect the exit of any threads
  43. // that are begun via direct OS thread creation functions, nor can it detect the exit of
  44. // threads that are exited by direct OS thread exit functions. This is may not be a major
  45. // problem, because C/C++ programs should virtually always be calling the CRT thread begin
  46. // and end functions so that the CRT can be maintained properly for the thread.
  47. //
  48. // typedef void (*_PVFV)();
  49. // void __tlregdtor(_PVFV func);
  50. // void ThreadExit(){ Do something. May need to be careful about what APIs are called. }
  51. // Assertion variables.
  52. EA::Thread::AssertionFailureFunction gpAssertionFailureFunction = NULL;
  53. void* gpAssertionFailureContext = NULL;
  54. // Dynamic thread array maintenance.
  55. // If the user calls GetThreadId from a thread that was created by some third
  56. // party, then we don't have a thread handle for it. The only current way to get
  57. // such a thread handle is to call OpenThread(GetCurrentThreadId()) or
  58. // DuplicateHandle(GetCurrentThread()). In either case the return value is a
  59. // handle which must be disposed of via CloseHandle. Additionally, since the
  60. // thread was created by a thrid party, it's entirely possible that the thread
  61. // will be exited without us ever finding about it. But we still need to call
  62. // CloseHandle on the handle. So we maintain an array of handles and check their
  63. // status periodically and upon process exit.
  64. const size_t kMaxThreadDynamicArrayCount = 128;
  65. struct DynamicThreadArray
  66. {
  67. static HANDLE mhDynamicThreadArray[kMaxThreadDynamicArrayCount];
  68. static CRITICAL_SECTION mCriticalSection;
  69. static bool mbDynamicThreadArrayInitialized;
  70. static void Initialize();
  71. static void CheckDynamicThreadArray(bool bCloseAll);
  72. static void AddDynamicThreadHandle(HANDLE hThread, bool bAdd);
  73. };
  74. HANDLE DynamicThreadArray::mhDynamicThreadArray[kMaxThreadDynamicArrayCount];
  75. CRITICAL_SECTION DynamicThreadArray::mCriticalSection;
  76. bool DynamicThreadArray::mbDynamicThreadArrayInitialized;
  77. // DynamicThreadArray ctor/dtor were removed to because memory tracking systems that are required to run
  78. // pre-main and post-main. In order to support memory tracking of allocations that occur post-main we
  79. // intentially "leak" a operating system critical section and leave it to be cleaned up by the operating
  80. // system at process shutdown.
  81. //
  82. // DynamicThreadArray::DynamicThreadArray()
  83. // {
  84. // Initialize();
  85. // }
  86. // DynamicThreadArray::~DynamicThreadArray()
  87. // {
  88. // CheckDynamicThreadArray(true);
  89. // DeleteCriticalSection(&mCriticalSection);
  90. // }
  91. void DynamicThreadArray::Initialize()
  92. {
  93. static EA::Thread::Futex m;
  94. const bool done = mbDynamicThreadArrayInitialized;
  95. // ensure that if we've seen previous writes to mbDynamicThreadArrayInitialized, we also
  96. // see the writes to mCriticalSection, to avoid the case where another thread sees the flag
  97. // before it sees the initialization
  98. EAReadBarrier();
  99. if(!done)
  100. {
  101. EA::Thread::AutoFutex _(m);
  102. if (!mbDynamicThreadArrayInitialized)
  103. {
  104. memset(mhDynamicThreadArray, 0, sizeof(mhDynamicThreadArray));
  105. InitializeCriticalSection(&mCriticalSection);
  106. // ensure writes to mCriticalSection and mhDynamicThreadArray are visible before writes
  107. // to mbDynamicThreadArrayInitialized, to avoid the case where another thread sees the
  108. // flag before it sees the initialization
  109. EAWriteBarrier();
  110. mbDynamicThreadArrayInitialized = true;
  111. }
  112. }
  113. }
  114. // This function looks at the existing set of thread ids and see if any of them
  115. // were quit. If so then this function removes their entry from our array of
  116. // thread handles, and most importantly, calls CloseHandle on the thread handle.
  117. void DynamicThreadArray::CheckDynamicThreadArray(bool bCloseAll)
  118. {
  119. Initialize();
  120. EnterCriticalSection(&mCriticalSection);
  121. for(size_t i(0); i < sizeof(mhDynamicThreadArray)/sizeof(mhDynamicThreadArray[0]); i++)
  122. {
  123. if(mhDynamicThreadArray[i])
  124. {
  125. DWORD dwExitCode(0);
  126. // Note that GetExitCodeThread is a hazard if the user of a thread exits
  127. // with a return value that is equal to the value of STILL_ACTIVE (i.e. 259).
  128. // We can document that users shouldn't do this, or we can change the code
  129. // here to use WaitForSingleObject(hThread, 0) and assume the thread is
  130. // still active if the return value is WAIT_TIMEOUT.
  131. if(bCloseAll || !GetExitCodeThread(mhDynamicThreadArray[i], &dwExitCode) || (dwExitCode != STILL_ACTIVE)) // If the thread id is invalid or it has exited...
  132. {
  133. CloseHandle(mhDynamicThreadArray[i]); // This matches the DuplicateHandle call we made below.
  134. mhDynamicThreadArray[i] = 0;
  135. }
  136. }
  137. }
  138. LeaveCriticalSection(&mCriticalSection);
  139. }
  140. void DynamicThreadArray::AddDynamicThreadHandle(HANDLE hThread, bool bAdd)
  141. {
  142. Initialize();
  143. if(hThread)
  144. {
  145. EnterCriticalSection(&mCriticalSection);
  146. if(bAdd)
  147. {
  148. for(size_t i(0); i < sizeof(mhDynamicThreadArray)/sizeof(mhDynamicThreadArray[0]); i++)
  149. {
  150. if(mhDynamicThreadArray[i] == kThreadIdInvalid)
  151. {
  152. mhDynamicThreadArray[i] = hThread;
  153. hThread = kThreadIdInvalid; // This tells us that we succeeded, and we'll use this result below.
  154. break;
  155. }
  156. }
  157. EAT_ASSERT(hThread == kThreadIdInvalid); // Assert that there was enough room (that the above loop found a spot).
  158. if(hThread != kThreadIdInvalid) // If not, then we need to free the handle.
  159. CloseHandle(hThread); // This matches the DuplicateHandle call we made below.
  160. }
  161. else
  162. {
  163. for(size_t i(0); i < sizeof(mhDynamicThreadArray)/sizeof(mhDynamicThreadArray[0]); i++)
  164. {
  165. if(mhDynamicThreadArray[i] == hThread)
  166. {
  167. CloseHandle(hThread); // This matches the DuplicateHandle call we made below.
  168. mhDynamicThreadArray[i] = kThreadIdInvalid;
  169. break;
  170. }
  171. }
  172. // By design, we don't consider a non-found handle an error. It may simply be the
  173. // case that the given handle was not a dynamnic thread handle. Due to the way
  174. // Windows works, there's just no way for us to tell.
  175. }
  176. LeaveCriticalSection(&mCriticalSection);
  177. }
  178. }
  179. // Thread handle local storage.
  180. // We have this code here in order to cache the thread handles for
  181. // threads, so that the user gets a consistent return value from the
  182. // GetThreadId function for each unique thread.
  183. static DWORD dwThreadHandleTLS = TLS_OUT_OF_INDEXES; // We intentionally make this an independent variable so that it is initialized unilaterally on segment load.
  184. struct TLSAlloc
  185. {
  186. TLSAlloc()
  187. {
  188. if(dwThreadHandleTLS == TLS_OUT_OF_INDEXES) // It turns out that the user might have set this to a
  189. dwThreadHandleTLS = TlsAlloc(); // value before this constructor has run. So we check.
  190. }
  191. #if EATHREAD_TLSALLOC_DTOR_ENABLED
  192. // Since this class is used only as a static variable, this destructor would
  193. // only get called during module destruction: app quit or DLL unload.
  194. // In the case of DLL unload, we may have a problem if the DLL was unloaded
  195. // before threads created by it were destroyed. Whether the problem is significant
  196. // depends on the application. In most cases it won't be significant.
  197. //
  198. // We want to call TlsFree because not doing so results in a memory leak and eventual
  199. // exhaustion of TLS ids by the system.
  200. ~TLSAlloc()
  201. {
  202. if(dwThreadHandleTLS != TLS_OUT_OF_INDEXES)
  203. {
  204. // We don't read the hThread stored at dwThreadHandleTLS and call CloseHandle
  205. // on it, as the DynamicThreadArray destructor will deal with closing any
  206. // thread handles this module knows about.
  207. TlsFree(dwThreadHandleTLS);
  208. dwThreadHandleTLS = TLS_OUT_OF_INDEXES;
  209. }
  210. }
  211. #endif
  212. };
  213. static TLSAlloc sTLSAlloc;
  214. void SetCurrentThreadHandle(HANDLE hThread, bool bDynamic)
  215. {
  216. // EAT_ASSERT(hThread != kThreadIdInvalid); We can't do this, as we can be intentionally called with an hThread of kThreadIdInvalid.
  217. if(dwThreadHandleTLS == TLS_OUT_OF_INDEXES) // This should generally always evaluate to true because we init dwThreadHandleTLS on startup.
  218. dwThreadHandleTLS = TlsAlloc();
  219. EAT_ASSERT(dwThreadHandleTLS != TLS_OUT_OF_INDEXES);
  220. if(dwThreadHandleTLS != TLS_OUT_OF_INDEXES)
  221. {
  222. DynamicThreadArray::CheckDynamicThreadArray(false);
  223. if(bDynamic)
  224. {
  225. if(hThread != kThreadIdInvalid) // If adding the hThread...
  226. DynamicThreadArray::AddDynamicThreadHandle(hThread, true);
  227. else // Else removing the existing current thread handle...
  228. {
  229. HANDLE hThreadOld = TlsGetValue(dwThreadHandleTLS);
  230. if(hThreadOld != kThreadIdInvalid) // This should always evaluate to true in practice.
  231. DynamicThreadArray::AddDynamicThreadHandle(hThreadOld, false); // Will Close the dynamic thread handle if it is one.
  232. }
  233. }
  234. TlsSetValue(dwThreadHandleTLS, hThread);
  235. }
  236. }
  237. } // namespace Thread
  238. } // namespace EA
  239. EATHREADLIB_API EA::Thread::ThreadId EA::Thread::GetThreadId()
  240. {
  241. // We have some non-trivial code here because Windows doesn't provide a means for a
  242. // thread to read its own thread id (thread handle) in a consistent way.
  243. // If we have allocated thread-local storage for this module...
  244. if(dwThreadHandleTLS != TLS_OUT_OF_INDEXES)
  245. {
  246. void* const pValue = TlsGetValue(dwThreadHandleTLS);
  247. if(pValue) // If the current thread's ThreadId has been previously saved...
  248. return pValue; // Under Win32, type ThreadId should be the same as HANDLE which should be the same as void*.
  249. // Else fall through and get the current thread handle and cache it so that next time the above code will succeed.
  250. }
  251. // In this case the thread was not created by EAThread. So we give
  252. // the thread a new Id, based on GetCurrentThread and DuplicateHandle.
  253. // GetCurrentThread returns a "pseudo handle" which isn't actually the
  254. // thread handle but is a hard-coded constant which means "current thread".
  255. // If you want to get a real thread handle then you need to call DuplicateHandle
  256. // on the pseudo handle. Every time you call DuplicateHandle you get a different
  257. // result, yet we want this GetThreadId function to return a consistent value
  258. // to the user, as that's what a rational user would expect. So after calling
  259. // DuplicateHandle we save the value for the next time the user calls this
  260. // function. We save the value in thread-local storage, so each unique thread
  261. // sees a unique view of GetThreadId.
  262. HANDLE hThread, hThreadPseudo = GetCurrentThread();
  263. BOOL bResult = DuplicateHandle(GetCurrentProcess(), hThreadPseudo, GetCurrentProcess(), &hThread, 0, true, DUPLICATE_SAME_ACCESS);
  264. EAT_ASSERT(bResult && (hThread != kThreadIdInvalid));
  265. if(bResult)
  266. EA::Thread::SetCurrentThreadHandle(hThread, true); // Need to eventually call CloseHandle on hThread, so we store it.
  267. return hThread;
  268. }
  269. EATHREADLIB_API EA::Thread::ThreadId EA::Thread::GetThreadId(EA::Thread::SysThreadId id)
  270. {
  271. EAThreadDynamicData* const pTDD = EA::Thread::FindThreadDynamicData(id);
  272. if(pTDD)
  273. {
  274. return pTDD->mhThread;
  275. }
  276. return EA::Thread::kThreadIdInvalid;
  277. }
  278. EATHREADLIB_API EA::Thread::SysThreadId EA::Thread::GetSysThreadId(ThreadId id)
  279. {
  280. #if defined(EA_PLATFORM_MICROSOFT) && defined(EA_PROCESSOR_X86_64)
  281. // Win64 has this function natively.
  282. return ::GetThreadId(id);
  283. // Fast implementation of this, which has been verified:
  284. // uintptr_t pTIB = __readgsqword(0x30);
  285. // uint32_t threadId = *((uint32_t*)(((uint8_t*)pTIB) + 0x48));
  286. // return (EA::Thread::SysThreadId)threadId;
  287. #elif defined(EA_PLATFORM_WIN32)
  288. // What we do here is first try to use the GetThreadId function, which is
  289. // available on some later versions of WinXP and later OSs. If that doesn't
  290. // work then we are using an earlier OS and we use the NtQueryInformationThread
  291. // kernel function to read thread info.
  292. typedef DWORD (WINAPI *GetThreadIdFunc)(HANDLE);
  293. typedef BOOL (WINAPI *NtQueryInformationThreadFunc)(HANDLE, int, PVOID, ULONG, PULONG);
  294. // We implement our own manual version of static variables here. We do this because
  295. // the static variable mechanism the compiler provides wouldn't provide thread
  296. // safety for us.
  297. static volatile bool sInitialized = false;
  298. static GetThreadIdFunc spGetThreadIdFunc = NULL;
  299. static NtQueryInformationThreadFunc spNtQueryInformationThread = NULL;
  300. if(!sInitialized)
  301. {
  302. HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
  303. if(hKernel32)
  304. spGetThreadIdFunc = (GetThreadIdFunc)(uintptr_t)GetProcAddress(hKernel32, "GetThreadId");
  305. if(!spGetThreadIdFunc)
  306. {
  307. HMODULE hNTDLL = GetModuleHandleA("ntdll.dll");
  308. if(hNTDLL)
  309. spNtQueryInformationThread = (NtQueryInformationThreadFunc)(uintptr_t)GetProcAddress(hNTDLL, "NtQueryInformationThread");
  310. }
  311. sInitialized = true;
  312. }
  313. if(spGetThreadIdFunc)
  314. return (SysThreadId)spGetThreadIdFunc(id);
  315. if(spNtQueryInformationThread)
  316. {
  317. struct THREAD_BASIC_INFORMATION_WIN32
  318. {
  319. BOOL ExitStatus;
  320. PVOID TebBaseAddress;
  321. DWORD UniqueProcessId;
  322. DWORD UniqueThreadId;
  323. DWORD AffinityMask;
  324. DWORD Priority;
  325. DWORD BasePriority;
  326. };
  327. THREAD_BASIC_INFORMATION_WIN32 tbi;
  328. if(spNtQueryInformationThread(id, 0, &tbi, sizeof(tbi), NULL) == 0)
  329. return (SysThreadId)tbi.UniqueThreadId;
  330. }
  331. return kSysThreadIdInvalid;
  332. #endif
  333. }
  334. EATHREADLIB_API EA::Thread::SysThreadId EA::Thread::GetSysThreadId()
  335. {
  336. return ::GetCurrentThreadId();
  337. }
  338. EATHREADLIB_API int EA::Thread::GetThreadPriority()
  339. {
  340. const int nPriority = ::GetThreadPriority(GetCurrentThread());
  341. return kThreadPriorityDefault + (nPriority - THREAD_PRIORITY_NORMAL);
  342. }
  343. EATHREADLIB_API bool EA::Thread::SetThreadPriority(int nPriority)
  344. {
  345. EAT_ASSERT(nPriority != kThreadPriorityUnknown);
  346. int nNewPriority = THREAD_PRIORITY_NORMAL + (nPriority - kThreadPriorityDefault);
  347. bool result = ::SetThreadPriority(GetCurrentThread(), nNewPriority) != 0;
  348. // Windows process running in NORMAL_PRIORITY_CLASS is picky about the priority passed in.
  349. // So we need to set the priority to the next priority supported
  350. #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE)
  351. HANDLE thread = GetCurrentThread();
  352. while(!result)
  353. {
  354. if (nNewPriority >= THREAD_PRIORITY_TIME_CRITICAL)
  355. return ::SetThreadPriority(thread, THREAD_PRIORITY_TIME_CRITICAL) != 0;
  356. if (nNewPriority <= THREAD_PRIORITY_IDLE)
  357. return ::SetThreadPriority(thread, THREAD_PRIORITY_IDLE) != 0;
  358. result = ::SetThreadPriority(thread, nNewPriority) != 0;
  359. nNewPriority++;
  360. }
  361. #endif
  362. return result;
  363. }
  364. EATHREADLIB_API void EA::Thread::SetThreadProcessor(int nProcessor)
  365. {
  366. #if defined(EA_PLATFORM_XBOXONE)
  367. DWORD mask = 0xFF; //Default to all
  368. if (nProcessor >= 0)
  369. mask = (DWORD)(1 << nProcessor);
  370. SetThreadAffinityMask(GetCurrentThread(), mask);
  371. #else
  372. static const int nProcessorCount = GetProcessorCount();
  373. if(nProcessor < 0)
  374. nProcessor = MAXIMUM_PROCESSORS; // This cases the SetThreadIdealProcessor to reset to 'no ideal processor'.
  375. else
  376. {
  377. if(nProcessor >= nProcessorCount)
  378. nProcessor %= nProcessorCount;
  379. }
  380. // SetThreadIdealProcessor differs from SetThreadAffinityMask in that SetThreadIdealProcessor is not
  381. // a strict assignment, and it allows the OS to move the thread if the ideal processor is busy.
  382. // SetThreadAffinityMask is a more rigid assignment, but it can result in slower performance and
  383. // possibly hangs due to processor contention between threads. For Windows we use SetIdealThreadProcessor
  384. // in the name of safety and likely better overall performance.
  385. SetThreadIdealProcessor(GetCurrentThread(), (DWORD)nProcessor);
  386. #endif
  387. }
  388. void* EA::Thread::GetThreadStackBase()
  389. {
  390. #if defined(EA_PLATFORM_WIN32) && defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_MSVC)
  391. // Offset 0x18 from the FS segment register gives a pointer to
  392. // the thread information block for the current thread
  393. // VC++ also offers the __readfsdword() intrinsic, which would be better to use here.
  394. NT_TIB* pTib;
  395. __asm {
  396. mov eax, fs:[18h]
  397. mov pTib, eax
  398. }
  399. return (void*)pTib->StackBase;
  400. #elif defined(EA_PLATFORM_MICROSOFT) && defined(EA_PROCESSOR_X86_64) && defined(EA_COMPILER_MSVC)
  401. // VC++ also offers the __readgsdword() intrinsic, which is an alternative which could
  402. // retrieve the current thread TEB if the following proves unreliable.
  403. PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
  404. return (void*)pTib->StackBase;
  405. #elif defined(EA_PLATFORM_WIN32) && defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_GCC)
  406. NT_TIB* pTib;
  407. asm ( "movl %%fs:0x18, %0\n"
  408. : "=r" (pTib)
  409. );
  410. return (void*)pTib->StackBase;
  411. #endif
  412. }
  413. #if defined(EA_PLATFORM_WIN32) && defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1400)
  414. // People report on the Internet that this function can get you what CPU the current thread
  415. // is running on. But that's false, as this function has been seen to return values greater than
  416. // the number of physical or real CPUs present. For example, this function returns 6 for my
  417. // Single CPU that's dual-hyperthreaded.
  418. static int GetCurrentProcessorNumberCPUID()
  419. {
  420. _asm { mov eax, 1 }
  421. _asm { cpuid }
  422. _asm { shr ebx, 24 }
  423. _asm { mov eax, ebx }
  424. }
  425. int GetCurrentProcessorNumberXP()
  426. {
  427. int cpuNumber = GetCurrentProcessorNumberCPUID();
  428. int cpuCount = EA::Thread::GetProcessorCount();
  429. return (cpuNumber % cpuCount); // I don't know if this is the right thing to do, but it's better than returning an impossible number and Windows XP is a fading OS as it is.
  430. }
  431. #endif
  432. EATHREADLIB_API int EA::Thread::GetThreadProcessor()
  433. {
  434. #if defined(EA_PLATFORM_WIN32)
  435. // Only Windows Vista and later provides GetCurrentProcessorNumber.
  436. // So we must dynamically link to this function.
  437. static EA_THREAD_LOCAL bool bInitialized = false;
  438. static EA_THREAD_LOCAL DWORD (WINAPI *pfnGetCurrentProcessorNumber)() = NULL;
  439. if(!bInitialized)
  440. {
  441. HMODULE hKernel32 = GetModuleHandleA("KERNEL32.DLL");
  442. if(hKernel32)
  443. pfnGetCurrentProcessorNumber = (DWORD (WINAPI*)())(uintptr_t)GetProcAddress(hKernel32, "GetCurrentProcessorNumber");
  444. bInitialized = true;
  445. }
  446. if(pfnGetCurrentProcessorNumber)
  447. return (int)(unsigned)pfnGetCurrentProcessorNumber();
  448. #if defined(EA_PLATFORM_WINDOWS) && defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_MSVC) && (EA_COMPILER_MSVC >= 1400)
  449. return GetCurrentProcessorNumberXP();
  450. #else
  451. return 0;
  452. #endif
  453. #elif defined(EA_PLATFORM_WIN64)
  454. static EA_THREAD_LOCAL bool bInitialized = false;
  455. static EA_THREAD_LOCAL DWORD (WINAPI *pfnGetCurrentProcessorNumber)() = NULL;
  456. if(!bInitialized)
  457. {
  458. HMODULE hKernel32 = GetModuleHandleA("KERNEL32.DLL"); // Yes, we want to use Kernel32.dll. There is no Kernel64.dll on Win64.
  459. if(hKernel32)
  460. pfnGetCurrentProcessorNumber = (DWORD (WINAPI*)())(uintptr_t)GetProcAddress(hKernel32, "GetCurrentProcessorNumber");
  461. bInitialized = true;
  462. }
  463. if(pfnGetCurrentProcessorNumber)
  464. return (int)(unsigned)pfnGetCurrentProcessorNumber();
  465. return 0;
  466. #else
  467. return (int)(unsigned)GetCurrentProcessorNumber();
  468. #endif
  469. }
  470. EATHREADLIB_API void EA::Thread::SetThreadAffinityMask(const EA::Thread::ThreadId& id, ThreadAffinityMask nAffinityMask)
  471. {
  472. // Update the affinity mask in the thread dynamic data cache.
  473. EAThreadDynamicData* const pTDD = FindThreadDynamicData(id);
  474. if(pTDD)
  475. {
  476. pTDD->mnThreadAffinityMask = nAffinityMask;
  477. }
  478. #if EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED
  479. #if defined(EA_PLATFORM_XBOXONE)
  480. DWORD_PTR nProcessorCountMask = 0x7F; // default to all 7 available cores.
  481. #else
  482. DWORD_PTR nProcessorCountMask = (DWORD_PTR)1 << GetProcessorCount();
  483. #endif
  484. // Call the Windows library function.
  485. DWORD_PTR nProcessAffinityMask, nSystemAffinityMask;
  486. if(EA_LIKELY(GetProcessAffinityMask(GetCurrentProcess(), &nProcessAffinityMask, &nSystemAffinityMask)))
  487. nProcessorCountMask = nProcessAffinityMask;
  488. nAffinityMask &= nProcessorCountMask;
  489. auto opResult = ::SetThreadAffinityMask(id, static_cast<DWORD_PTR>(nAffinityMask));
  490. EA_UNUSED(opResult);
  491. EAT_ASSERT_FORMATTED(opResult != 0, "The Windows platform SetThreadAffinityMask failed. GetLastError %x", GetLastError());
  492. #endif
  493. }
  494. EATHREADLIB_API EA::Thread::ThreadAffinityMask EA::Thread::GetThreadAffinityMask(const EA::Thread::ThreadId& id)
  495. {
  496. // Update the affinity mask in the thread dynamic data cache.
  497. EAThreadDynamicData* const pTDD = FindThreadDynamicData(id);
  498. if(pTDD)
  499. {
  500. return pTDD->mnThreadAffinityMask;
  501. }
  502. return kThreadAffinityMaskAny;
  503. }
  504. // Internal SetThreadName API's so we don't repeat the implementations
  505. namespace EA {
  506. namespace Thread {
  507. namespace Internal {
  508. bool PixSetThreadName(EA::Thread::ThreadId threadId, const char* pName)
  509. {
  510. EA_UNUSED(threadId); EA_UNUSED(pName);
  511. bool result = true;
  512. #if (defined(EA_PLATFORM_XBOXONE) && EA_CAPILANO_DBG_ENABLED == 1)
  513. wchar_t wName[EATHREAD_NAME_SIZE];
  514. mbstowcs(wName, pName, EATHREAD_NAME_SIZE);
  515. result = (::SetThreadName(threadId, wName) == TRUE); // requires toolhelpx.lib
  516. EAT_ASSERT(result);
  517. #endif
  518. return result;
  519. }
  520. bool WinSetThreadName(EA::Thread::ThreadId threadId, const char* pName)
  521. {
  522. bool result = true;
  523. typedef HRESULT(WINAPI *SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
  524. // Check if Windows Operating System has the 'SetThreadDescription" API.
  525. auto pSetThreadDescription = (SetThreadDescription)GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetThreadDescription");
  526. if (pSetThreadDescription)
  527. {
  528. wchar_t wName[EATHREAD_NAME_SIZE];
  529. mbstowcs(wName, pName, EATHREAD_NAME_SIZE);
  530. result = SUCCEEDED(pSetThreadDescription(threadId, wName));
  531. EAT_ASSERT(result);
  532. }
  533. return result;
  534. }
  535. void WinSetThreadNameByException(EA::Thread::SysThreadId threadId, const char* pName)
  536. {
  537. struct ThreadNameInfo
  538. {
  539. DWORD dwType;
  540. LPCSTR lpName;
  541. DWORD dwThreadId;
  542. DWORD dwFlags;
  543. };
  544. // This setjmp/longjmp weirdness is here to work around an apparent bug in the VS2013 debugger,
  545. // whereby EBX will be trashed on return from RaiseException, causing bad things to happen in code
  546. // which runs later. This only seems to happen when a debugger is attached and there's some managed
  547. // code in the process.
  548. jmp_buf jmpbuf;
  549. EA_DISABLE_VC_WARNING(4611)
  550. if (!setjmp(jmpbuf))
  551. {
  552. ThreadNameInfo threadNameInfo = {0x1000, pName, threadId, 0};
  553. __try { RaiseException(0x406D1388, 0, sizeof(threadNameInfo) / sizeof(ULONG_PTR), (CONST ULONG_PTR*)(uintptr_t)&threadNameInfo); }
  554. __except (GetExceptionCode() == 0x406D1388 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { }
  555. longjmp(jmpbuf, 1);
  556. }
  557. EA_RESTORE_VC_WARNING()
  558. }
  559. void SetThreadName(EAThreadDynamicData* pTDD, const char* pName)
  560. {
  561. strncpy(pTDD->mName, pName, EATHREAD_NAME_SIZE);
  562. pTDD->mName[EATHREAD_NAME_SIZE - 1] = 0;
  563. #if defined(EA_PLATFORM_WINDOWS) && defined(EA_COMPILER_MSVC) || (defined(EA_PLATFORM_XBOXONE))
  564. if(pTDD->mName[0] && (pTDD->mhThread != EA::Thread::kThreadIdInvalid))
  565. {
  566. #if EATHREAD_NAMING == EATHREAD_NAMING_DISABLED
  567. bool namingEnabled = false;
  568. #elif EATHREAD_NAMING == EATHREAD_NAMING_ENABLED
  569. bool namingEnabled = true;
  570. #else
  571. bool namingEnabled = IsDebuggerPresent();
  572. #endif
  573. if(namingEnabled)
  574. {
  575. PixSetThreadName(pTDD->mhThread, pTDD->mName);
  576. WinSetThreadName(pTDD->mhThread, pTDD->mName);
  577. WinSetThreadNameByException(pTDD->mnThreadId, pTDD->mName);
  578. }
  579. }
  580. #endif
  581. }
  582. } // namespace Internal
  583. } // namespace Thread
  584. } // namespace EA
  585. EATHREADLIB_API void EA::Thread::SetThreadName(const char* pName) { SetThreadName(GetThreadId(), pName); }
  586. EATHREADLIB_API const char* EA::Thread::GetThreadName() { return GetThreadName(GetThreadId()); }
  587. EATHREADLIB_API void EA::Thread::SetThreadName(const EA::Thread::ThreadId& id, const char* pName)
  588. {
  589. EAThreadDynamicData* const pTDD = FindThreadDynamicData(id);
  590. if(pTDD)
  591. {
  592. Internal::SetThreadName(pTDD, pName);
  593. }
  594. }
  595. EATHREADLIB_API const char* EA::Thread::GetThreadName(const EA::Thread::ThreadId& id)
  596. {
  597. EAThreadDynamicData* const pTDD = FindThreadDynamicData(id);
  598. return pTDD ? pTDD->mName : "";
  599. }
  600. EATHREADLIB_API int EA::Thread::GetProcessorCount()
  601. {
  602. #if defined(EA_PLATFORM_XBOXONE)
  603. // Capilano has 7-ish physical CPUs available to titles. We can access 50 - 90% of the 7th Core.
  604. // Check platform documentation for details.
  605. DWORD_PTR ProcessAffinityMask;
  606. DWORD_PTR SystemAffinityMask;
  607. unsigned long nCoreCount = 6;
  608. if(EA_LIKELY(GetProcessAffinityMask(GetCurrentProcess(), &ProcessAffinityMask, &SystemAffinityMask)))
  609. {
  610. _BitScanForward(&nCoreCount, (unsigned long)~ProcessAffinityMask);
  611. }
  612. return (int) nCoreCount;
  613. #elif defined(EA_PLATFORM_WINDOWS)
  614. static int nProcessorCount = 0; // This doesn't really need to be an atomic integer.
  615. if(nProcessorCount == 0)
  616. {
  617. // A better function to use would possibly be KeQueryActiveProcessorCount
  618. // (NTKERNELAPI ULONG KeQueryActiveProcessorCount(PKAFFINITY ActiveProcessors))
  619. SYSTEM_INFO systemInfo;
  620. memset(&systemInfo, 0, sizeof(systemInfo));
  621. GetSystemInfo(&systemInfo);
  622. nProcessorCount = (int)systemInfo.dwNumberOfProcessors;
  623. }
  624. return nProcessorCount;
  625. #else
  626. static int nProcessorCount = 0; // This doesn't really need to be an atomic integer.
  627. if(nProcessorCount == 0)
  628. {
  629. // A better function to use would possibly be KeQueryActiveProcessorCount
  630. // (NTKERNELAPI ULONG KeQueryActiveProcessorCount(PKAFFINITY ActiveProcessors))
  631. SYSTEM_INFO systemInfo;
  632. memset(&systemInfo, 0, sizeof(systemInfo));
  633. GetNativeSystemInfo(&systemInfo);
  634. nProcessorCount = (int)systemInfo.dwNumberOfProcessors;
  635. }
  636. return nProcessorCount;
  637. #endif
  638. }
  639. EATHREADLIB_API void EA::Thread::ThreadSleep(const ThreadTime& timeRelative)
  640. {
  641. // Sleep(0) sleeps the current thread if any other thread of equal priority is ready to run.
  642. // Sleep(n) sleeps the current thread for up to n milliseconds if there is any other thread of any priority ready to run.
  643. // SwitchToThread() sleeps the current thread for one time slice if there is any other thread of any priority ready to run.
  644. if(timeRelative == 0)
  645. SwitchToThread(); // It's debateable whether we should do a SwitchToThread or a Sleep(0) here.
  646. else // The only difference is that the former allows threads of lower priority to execute.
  647. SleepEx((unsigned)timeRelative, TRUE);
  648. }
  649. namespace EA {
  650. namespace Thread {
  651. extern EAThreadDynamicData* FindThreadDynamicData(ThreadId threadId);
  652. extern EAThreadDynamicData* FindThreadDynamicData(SysThreadId sysThreadId);
  653. }
  654. }
  655. void EA::Thread::ThreadEnd(intptr_t threadReturnValue)
  656. {
  657. EAThreadDynamicData* const pTDD = FindThreadDynamicData(GetThreadId());
  658. if(pTDD)
  659. {
  660. pTDD->mnStatus = Thread::kStatusEnded;
  661. pTDD->mnReturnValue = threadReturnValue;
  662. pTDD->Release();
  663. }
  664. EA::Thread::SetCurrentThreadHandle(kThreadIdInvalid, true); // We use 'true' here just to be safe, as we don't know who is calling this function.
  665. #if defined(EA_PLATFORM_XBOXONE)
  666. // _endthreadex is not supported on Capilano because it's not compatible with C++/CX and /ZW. Use of ExitThread could result in memory leaks
  667. // as ExitThread does not clean up memory allocated by the C runtime library.
  668. // https://forums.xboxlive.com/AnswerPage.aspx?qid=47c1607c-bb18-4bc4-a79a-a40c59444ff3&tgt=1
  669. ExitThread(static_cast<DWORD>(threadReturnValue));
  670. #elif defined(EA_PLATFORM_MICROSOFT) && defined(EA_PLATFORM_CONSOLE) && !defined(EA_PLATFORM_XBOXONE)
  671. EAT_FAIL_MSG("EA::Thread::ThreadEnd: Not supported by this platform.");
  672. #else
  673. _endthreadex((unsigned int)threadReturnValue);
  674. #endif
  675. }
  676. EATHREADLIB_API EA::Thread::ThreadTime EA::Thread::GetThreadTime()
  677. {
  678. // We choose to use GetTickCount because it low overhead and
  679. // still yields values that have a precision in the same range
  680. // as the Win32 thread time slice time. In particular:
  681. // rdtsc takes ~5 cycles and has a nanosecond resolution. But it is unreliable
  682. // GetTickCount() takes ~80 cycles and has ~15ms resolution.
  683. // timeGetTime() takes ~350 cpu cycles and has 1ms resolution.
  684. // QueryPerformanceCounter() takes ~3000 cpu cycles on most machines and has ~1us resolution.
  685. // We add EATHREAD_MIN_ABSOLUTE_TIME to this absolute time to ensure this absolute time is never less than our min
  686. // (This fix was required because GetTickCount64 starts at 0x0 for titles on capilano)
  687. #if defined(EA_PLATFORM_MICROSOFT) && defined(EA_PROCESSOR_X86_64)
  688. return (ThreadTime)(GetTickCount64() + EATHREAD_MIN_ABSOLUTE_TIME);
  689. #else // Note that this value matches the value used by some runtime assertion code within EA::Thread. It would be best to define this as a shared constant between modules.
  690. return (ThreadTime)(GetTickCount() + EATHREAD_MIN_ABSOLUTE_TIME);
  691. #endif
  692. }
  693. EATHREADLIB_API void EA::Thread::SetAssertionFailureFunction(EA::Thread::AssertionFailureFunction pAssertionFailureFunction, void* pContext)
  694. {
  695. gpAssertionFailureFunction = pAssertionFailureFunction;
  696. gpAssertionFailureContext = pContext;
  697. }
  698. EATHREADLIB_API void EA::Thread::AssertionFailure(const char* pExpression)
  699. {
  700. if(gpAssertionFailureFunction)
  701. gpAssertionFailureFunction(pExpression, gpAssertionFailureContext);
  702. else
  703. {
  704. #if EAT_ASSERT_ENABLED
  705. OutputDebugStringA("EA::Thread::AssertionFailure: ");
  706. OutputDebugStringA(pExpression);
  707. OutputDebugStringA("\n");
  708. #ifdef EA_COMPILER_MSVC
  709. __debugbreak();
  710. #endif
  711. #endif
  712. }
  713. }
  714. uint32_t EA::Thread::RelativeTimeoutFromAbsoluteTimeout(ThreadTime timeoutAbsolute)
  715. {
  716. EAT_ASSERT((timeoutAbsolute == kTimeoutImmediate) || (timeoutAbsolute > EATHREAD_MIN_ABSOLUTE_TIME)); // Assert that the user didn't make the mistake of treating time as relative instead of absolute.
  717. DWORD timeoutRelative = 0;
  718. if (timeoutAbsolute == kTimeoutNone)
  719. {
  720. timeoutRelative = INFINITE;
  721. }
  722. else if (timeoutAbsolute == kTimeoutImmediate)
  723. {
  724. timeoutRelative = 0;
  725. }
  726. else
  727. {
  728. ThreadTime timeCurrent(GetThreadTime());
  729. timeoutRelative = (timeoutAbsolute > timeCurrent) ? static_cast<DWORD>(timeoutAbsolute - timeCurrent) : 0;
  730. }
  731. EAT_ASSERT((timeoutRelative == INFINITE) || (timeoutRelative < 100000000)); // Assert that the timeout is a sane value and didn't wrap around.
  732. return timeoutRelative;
  733. }
  734. #endif // EA_PLATFORM_XXX