eathread_unix.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include <EABase/eabase.h>
  5. #include <EABase/eahave.h>
  6. #include <eathread/eathread.h>
  7. #include <eathread/eathread_thread.h>
  8. #if defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE
  9. #include <pthread.h>
  10. #include <sched.h>
  11. #include <string.h>
  12. #ifdef EA_PLATFORM_WINDOWS
  13. #pragma warning(push, 0)
  14. #include <Windows.h> // Presumably we are using pthreads-win32.
  15. #pragma warning(pop)
  16. #include <time.h>
  17. #else
  18. #include <unistd.h>
  19. #if defined(_YVALS)
  20. #include <time.h>
  21. #else
  22. #include <sys/time.h>
  23. #endif
  24. #if defined(EA_PLATFORM_OSX) || defined(EA_PLATFORM_BSD)
  25. #include <sys/sysctl.h>
  26. #include <sys/param.h>
  27. #endif
  28. #if defined(EA_PLATFORM_LINUX)
  29. #include <sys/prctl.h>
  30. #endif
  31. #if defined(EA_PLATFORM_APPLE) || defined(__APPLE__)
  32. #include <dlfcn.h>
  33. #endif
  34. #if defined(EA_PLATFORM_BSD) || defined(EA_PLATFORM_CONSOLE_BSD) || defined(__FreeBSD__)
  35. #include <sys/param.h>
  36. #include <pthread_np.h>
  37. #endif
  38. #if defined(EA_PLATFORM_ANDROID)
  39. #include <sys/syscall.h>
  40. #endif
  41. #endif
  42. namespace EA
  43. {
  44. namespace Thread
  45. {
  46. // Assertion variables.
  47. EA::Thread::AssertionFailureFunction gpAssertionFailureFunction = NULL;
  48. void* gpAssertionFailureContext = NULL;
  49. }
  50. }
  51. EA::Thread::ThreadId EA::Thread::GetThreadId()
  52. {
  53. return pthread_self();
  54. }
  55. EA::Thread::ThreadId EA::Thread::GetThreadId(EA::Thread::SysThreadId id)
  56. {
  57. EAThreadDynamicData* const pTDD = EA::Thread::FindThreadDynamicData(id);
  58. if(pTDD)
  59. {
  60. return pTDD->mThreadId;
  61. }
  62. return EA::Thread::kThreadIdInvalid;
  63. }
  64. int EA::Thread::GetThreadPriority()
  65. {
  66. int policy;
  67. sched_param param;
  68. ThreadId currentThreadId = pthread_self();
  69. int result = pthread_getschedparam(currentThreadId, &policy, &param);
  70. if(result == 0)
  71. {
  72. #if defined(EA_PLATFORM_LINUX) && !defined(__CYGWIN__)
  73. return kThreadPriorityDefault + param.sched_priority; // This works for both SCHED_OTHER, SCHED_RR, and SCHED_FIFO.
  74. #else
  75. #if defined(EA_PLATFORM_WINDOWS)
  76. if(param.sched_priority == THREAD_PRIORITY_NORMAL)
  77. return kThreadPriorityDefault;
  78. #elif !(defined(__CYGWIN__) || defined(CS_UNDEFINED_STRING))
  79. if(policy == SCHED_OTHER)
  80. return 0; // 0 is the only native priority permitted with the SCHED_OTHER scheduling scheme.
  81. #endif
  82. // The following needs to be tested on a Unix-by-Unix case.
  83. const int nMin = sched_get_priority_min(policy);
  84. const int nMax = sched_get_priority_max(policy);
  85. // Some implementations of Pthreads associate higher priorities with smaller
  86. // integer values. We hide this. To the user, a higher value must always
  87. // indicate higher priority.
  88. const int adjustDir = (nMin < nMax) ? 1 : -1;
  89. const int nativeBasePriority = (nMin + nMax) / 2;
  90. // EAThread_user_priority = +/-(native_priority - EAThread_native_priority_default)
  91. return adjustDir * (param.sched_priority - nativeBasePriority);
  92. #endif
  93. }
  94. return kThreadPriorityDefault;
  95. }
  96. bool EA::Thread::SetThreadPriority(int nPriority)
  97. {
  98. ThreadId currentThreadId = pthread_self();
  99. int policy;
  100. sched_param param;
  101. int result = -1;
  102. EAT_ASSERT(nPriority != kThreadPriorityUnknown);
  103. #if defined(EA_PLATFORM_LINUX) && !defined(__CYGWIN__)
  104. // We are assuming Kernel 2.6 and later behavior, but perhaps we should dynamically detect.
  105. // Linux supports three scheduling policies SCHED_OTHER, SCHED_RR, and SCHED_FIFO.
  106. // The process needs to be run with superuser privileges to use SCHED_RR or SCHED_FIFO.
  107. // Thread priorities for SCHED_OTHER do not exist; there is only one allowed thread priority: 0.
  108. // Thread priorities for SCHED_RR and SCHED_FIFO are limited to the range of [1, 99] (verified with Linux 2.6.17),
  109. // despite documentation on the Internet that refers to ranges of 0-99, 1-100, 1-140, etc.
  110. // Higher values in this range mean higher priority.
  111. // All of the SCHED_RR and SCHED_FIFO privileges are higher than anything running at SCHED_OTHER,
  112. // as they are considered to be real-time scheduling. A result of this is that there is no
  113. // such thing as having a thread of lower priority than normal; there are only higher real-time priorities.
  114. if(nPriority <= kThreadPriorityDefault)
  115. {
  116. policy = SCHED_OTHER;
  117. param.sched_priority = 0;
  118. }
  119. else
  120. {
  121. policy = SCHED_RR;
  122. param.sched_priority = (nPriority - kThreadPriorityDefault);
  123. }
  124. result = pthread_setschedparam(currentThreadId, policy, &param);
  125. #else
  126. // The following needs to be tested on a Unix-by-Unix case.
  127. result = pthread_getschedparam(currentThreadId, &policy, &param);
  128. if(result == 0)
  129. {
  130. // Cygwin does not support any scheduling policy other than SCHED_OTHER.
  131. #if !defined(__CYGWIN__)
  132. if(policy == SCHED_OTHER)
  133. policy = SCHED_FIFO;
  134. #endif
  135. int nMin = sched_get_priority_min(policy);
  136. int nMax = sched_get_priority_max(policy);
  137. int adjustDir = 1;
  138. // Some implementations of pthreads associate higher priorities with smaller integer values.
  139. // To the EAThread user, a higher value indicates a higher priority.
  140. if (nMin > nMax)
  141. {
  142. adjustDir = nMax;
  143. nMax = nMin;
  144. nMin = adjustDir;
  145. adjustDir = -1; // Translate user's desire for higher priority into a native lower value.
  146. }
  147. // native_priority = EAThread_native_priority_default +/- EAThread_user_priority.
  148. // This calculation sets the default to be in the middle of low and high, which might not be so for all platforms in practice.
  149. param.sched_priority = ((nMin + nMax) / 2) + (adjustDir * nPriority);
  150. // Clamp to min/max as appropriate for current scheduling policy
  151. if(param.sched_priority < nMin)
  152. param.sched_priority = nMin;
  153. else if(param.sched_priority > nMax)
  154. param.sched_priority = nMax;
  155. result = pthread_setschedparam(currentThreadId, policy, &param);
  156. }
  157. #endif
  158. return (result == 0);
  159. }
  160. void* EA::Thread::GetThreadStackBase()
  161. {
  162. #if defined(EA_PLATFORM_APPLE)
  163. pthread_t threadId = pthread_self();
  164. return pthread_get_stackaddr_np(threadId);
  165. #elif (EA_PLATFORM_SOLARIS)
  166. stack_t s;
  167. thr_stksegment(&s);
  168. return s.ss_sp; // Note that this is not the sp pointer (which would refer to the a location low in the stack address space). When returned by thr_stksegment(), ss_sp refers to the top (base) of the stack.
  169. #elif defined(__CYGWIN__)
  170. // Cygwin reserves pthread_attr_getstackaddr and pthread_attr_getstacksize for future use.
  171. // The solution here is probably to use the Windows implementation of this here.
  172. return 0;
  173. #else // Other Unix
  174. void* stackLow = NULL;
  175. size_t stackSize = 0;
  176. pthread_t threadId = pthread_self();
  177. pthread_attr_t sattr;
  178. pthread_attr_init(&sattr);
  179. #if defined(EA_PLATFORM_BSD) || defined(EA_PLATFORM_CONSOLE_BSD) || defined(__FreeBSD__)
  180. pthread_attr_get_np(threadId, &sattr);
  181. #elif defined(EA_HAVE_pthread_getattr_np_DECL)
  182. // Note: this function is non-portable; various Unix systems may have different np alternatives
  183. pthread_getattr_np(threadId, &sattr);
  184. #else
  185. EA_UNUSED(threadId);
  186. // What to do?
  187. #endif
  188. // See http://www.opengroup.org/onlinepubs/009695399/functions/pthread_attr_getstack.html
  189. // stackLow is a constant. It is not the current low location but rather is the lowest allowed location.
  190. pthread_attr_getstack(&sattr, &stackLow, &stackSize);
  191. pthread_attr_destroy(&sattr);
  192. return (char*)stackLow + stackSize;
  193. #endif
  194. }
  195. void EA::Thread::SetThreadProcessor(int nProcessor)
  196. {
  197. // Posix threading doesn't have the ability to set the processor.
  198. #if defined(EA_PLATFORM_WINDOWS)
  199. static int nProcessorCount = 0; // This doesn't really need to be an atomic integer.
  200. if(nProcessorCount == 0)
  201. {
  202. SYSTEM_INFO systemInfo;
  203. memset(&systemInfo, 0, sizeof(systemInfo));
  204. GetSystemInfo(&systemInfo);
  205. nProcessorCount = (int)systemInfo.dwNumberOfProcessors;
  206. }
  207. DWORD dwThreadAffinityMask;
  208. if((nProcessor < 0) || (nProcessor >= nProcessorCount))
  209. dwThreadAffinityMask = 0xffffffff;
  210. else
  211. dwThreadAffinityMask = 1 << nProcessor;
  212. SetThreadAffinityMask(GetCurrentThread(), dwThreadAffinityMask);
  213. #elif (defined(EA_PLATFORM_LINUX) && !defined(EA_PLATFORM_ANDROID)) || defined(CS_UNDEFINED_STRING)
  214. cpu_set_t cpus;
  215. CPU_ZERO(&cpus);
  216. CPU_SET(nProcessor, &cpus);
  217. for (int c = 0; c < EA::Thread::GetProcessorCount(); c++, nProcessor >>= 1)
  218. {
  219. if (nProcessor & 1)
  220. {
  221. CPU_SET(c, &cpus);
  222. }
  223. }
  224. // To consider: Make it so we return a value.
  225. /*int result =*/ pthread_setaffinity_np(pthread_self(), sizeof(cpus), &cpus);
  226. // We don't assert on the success, as that could be very noisy for some users.
  227. #else
  228. // Other Unix platforms don't provide a means to specify what processor a thread runs on.
  229. // You have no choice but to let the OS schedule threads for you.
  230. EA_UNUSED(nProcessor);
  231. #endif
  232. }
  233. #if defined(EA_PLATFORM_WINDOWS) && defined(EA_PROCESSOR_X86) && defined(MSC_VER) && (MSC_VER >= 1400)
  234. int GetCurrentProcessorNumberXP()
  235. {
  236. _asm { mov eax, 1 }
  237. _asm { cpuid }
  238. _asm { shr ebx, 24 }
  239. _asm { mov eax, ebx }
  240. }
  241. #endif
  242. int EA::Thread::GetThreadProcessor()
  243. {
  244. #if defined(EA_PLATFORM_WINDOWS)
  245. // We are using Posix threading on Windows. It happens to be mapped to Windows threading and
  246. // so we can use Windows facilities to tell what processor the thread is running on.
  247. // Only Windows Vista and later provides GetCurrentProcessorNumber.
  248. // So we must dynamically link to this function.
  249. static EA_THREAD_LOCAL bool bInitialized = false;
  250. static EA_THREAD_LOCAL DWORD (WINAPI *pfnGetCurrentProcessorNumber)() = NULL;
  251. if(!bInitialized)
  252. {
  253. HMODULE hKernel32 = GetModuleHandle("KERNEL32.DLL");
  254. if(hKernel32)
  255. pfnGetCurrentProcessorNumber = (DWORD (WINAPI*)())GetProcAddress(hKernel32, "GetCurrentProcessorNumber");
  256. bInitialized = true;
  257. }
  258. if(pfnGetCurrentProcessorNumber)
  259. return (int)(unsigned)pfnGetCurrentProcessorNumber();
  260. #if defined(EA_PLATFORM_WINDOWS) && defined(EA_PROCESSOR_X86) && defined(MSC_VER) && (MSC_VER >= 1400)
  261. return GetCurrentProcessorNumberXP();
  262. #else
  263. return 0;
  264. #endif
  265. #elif defined(EA_PLATFORM_ANDROID)
  266. // return zero until Google provides a alternative to smp_processor_id()
  267. return 0;
  268. #elif EA_VALGRIND_ENABLED
  269. // Valgrind does not support the sched_getcpu() vsyscall. It causes it to detect a segfault in the program and stop it.
  270. // https://bugs.kde.org/show_bug.cgi?id=187043
  271. // http://git.dorsal.polymtl.ca/?p=ust.git;a=commitdiff_plain;h=8f09cb9340387a52b483752c5d2d6c36035b26bc
  272. return 0;
  273. #elif (defined(EA_PLATFORM_LINUX) && (defined(EATHREAD_GLIBC_VERSION) && (EATHREAD_GLIBC_VERSION > 2005)))
  274. // http://www.kernel.org/doc/man-pages/online/pages/man3/sched_getcpu.3.html
  275. // http://www.kernel.org/doc/man-pages/online/pages/man2/getcpu.2.html
  276. // Another solution is to use the cpuid instruction like we do for Windows.
  277. int cpu = sched_getcpu();
  278. if(cpu < 0)
  279. cpu = 0;
  280. if(cpu >= 0)
  281. return cpu;
  282. // Ideally we would never need to execute the following code:
  283. cpu_set_t cpus;
  284. CPU_ZERO(&cpus);
  285. pthread_getaffinity_np(pthread_self(), sizeof(cpus), &cpus);
  286. for(int i = 0; i < CPU_SETSIZE; i++)
  287. {
  288. if(CPU_ISSET(i, &cpus))
  289. return i;
  290. }
  291. return 0;
  292. #else
  293. return 0;
  294. #endif
  295. }
  296. EATHREADLIB_API void EA::Thread::SetThreadAffinityMask(const EA::Thread::ThreadId& id, ThreadAffinityMask nAffinityMask)
  297. {
  298. EAThreadDynamicData* const pTDD = FindThreadDynamicData(id);
  299. if(pTDD)
  300. {
  301. pTDD->mnThreadAffinityMask = nAffinityMask;
  302. #if EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED
  303. cpu_set_t cpuSetMask;
  304. memset(&cpuSetMask, 0, sizeof(cpu_set_t));
  305. for (int c = 0; c < EA::Thread::GetProcessorCount(); c++, nAffinityMask >>= 1)
  306. {
  307. if (nAffinityMask & 1)
  308. {
  309. CPU_SET(c, &cpuSetMask);
  310. }
  311. }
  312. sched_setaffinity(pTDD->mThreadPid, sizeof(cpu_set_t), &cpuSetMask);
  313. #endif
  314. }
  315. }
  316. EATHREADLIB_API EA::Thread::ThreadAffinityMask EA::Thread::GetThreadAffinityMask(const EA::Thread::ThreadId& id)
  317. {
  318. EAThreadDynamicData* const pTDD = FindThreadDynamicData(id);
  319. if(pTDD)
  320. {
  321. return pTDD->mnThreadAffinityMask;
  322. }
  323. return kThreadAffinityMaskAny;
  324. }
  325. // Internal SetThreadName API's so we don't repeat the implementations
  326. namespace Internal
  327. {
  328. // This function is not currently used if the thread name can be set from any other thread
  329. #if !EATHREAD_OTHER_THREAD_NAMING_SUPPORTED
  330. void SetCurrentThreadName(const char8_t* pName)
  331. {
  332. #if defined(EA_PLATFORM_LINUX)
  333. // http://manpages.courier-mta.org/htmlman2/prctl.2.html
  334. // The Linux documentation says PR_SET_NAME sets the process name, but that
  335. // documentation is wrong and instead it sets the current thread name.
  336. // Also: http://0pointer.de/blog/projects/name-your-threads.html
  337. // Stefan Kost recently pointed me to the fact that the Linux system call prctl(PR_SET_NAME)
  338. // does not in fact change the process name, but the task name (comm field) -- in contrast
  339. // to what the man page suggests. That makes it very useful for naming threads, since you
  340. // can read back the name you set with PR_SET_NAME earlier from the /proc file system
  341. // (/proc/$PID/task/$TID/comm on newer kernels, /proc/$PID/task/$TID/stat's second field
  342. // on older kernels), and hence distinguish which thread might be responsible for the high
  343. // CPU load or similar problems.
  344. char8_t nameBuf[16]; // Limited to 16 bytes, null terminated if < 16 bytes
  345. strncpy(nameBuf, pName, sizeof(nameBuf));
  346. nameBuf[15] = 0;
  347. prctl(PR_SET_NAME, (unsigned long)nameBuf, 0, 0, 0);
  348. #elif defined(EA_PLATFORM_APPLE) || defined(__APPLE__)
  349. // http://src.chromium.org/viewvc/chrome/trunk/src/base/platform_thread_mac.mm?revision=49465&view=markup&pathrev=49465
  350. // "There's a non-portable function for doing this: pthread_setname_np.
  351. // It's supported by OS X >= 10.6 and the Xcode debugger will show the thread
  352. // names if they're provided."
  353. // On OSX the return value is always -1 on error; use errno to tell the error value.
  354. typedef int (*pthread_setname_np_type)(const char*);
  355. pthread_setname_np_type pthread_setname_np_ptr = (pthread_setname_np_type)(uintptr_t)dlsym(RTLD_DEFAULT, "pthread_setname_np");
  356. if(pthread_setname_np_ptr)
  357. {
  358. // Mac OS X does not expose the length limit of the name, so hardcode it.
  359. char8_t nameBuf[63]; // It is not clear what the size limit actually is, though 63 is known to work because it was seen on the Internet.
  360. strncpy(nameBuf, pName, sizeof(nameBuf));
  361. nameBuf[62] = 0;
  362. pthread_setname_np_ptr(nameBuf);
  363. }
  364. #elif defined(EA_PLATFORM_BSD) || defined(EA_PLATFORM_CONSOLE_BSD) || defined(__FreeBSD__)
  365. // http://www.unix.com/man-page/freebsd/3/PTHREAD_SET_NAME_NP/
  366. pthread_set_name_np(pthread_self(), pName);
  367. #endif
  368. }
  369. #endif
  370. EA::Thread::ThreadId GetId(EAThreadDynamicData* pTDD)
  371. {
  372. if(pTDD)
  373. return pTDD->mThreadId;
  374. return EA::Thread::kThreadIdInvalid;
  375. }
  376. void SetThreadName(EAThreadDynamicData* pTDD)
  377. {
  378. #if defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_APPLE)
  379. EAT_COMPILETIME_ASSERT(EATHREAD_OTHER_THREAD_NAMING_SUPPORTED == 0);
  380. // http://stackoverflow.com/questions/2369738/can-i-set-the-name-of-a-thread-in-pthreads-linux
  381. // Under some Unixes you can name only the current thread, so we apply the naming
  382. // only if the currently executing thread is the one that is associated with
  383. // this class object.
  384. if(GetId(pTDD) == EA::Thread::GetThreadId())
  385. SetCurrentThreadName(pTDD->mName);
  386. #elif defined(EA_PLATFORM_BSD)
  387. EAT_COMPILETIME_ASSERT(EATHREAD_OTHER_THREAD_NAMING_SUPPORTED == 1);
  388. // http://www.unix.com/man-page/freebsd/3/PTHREAD_SET_NAME_NP/
  389. if(GetId(pTDD) != EA::Thread::kThreadIdInvalid)
  390. pthread_set_name_np(GetId(pTDD), pTDD->mName);
  391. #endif
  392. }
  393. } // namespace Internal
  394. EATHREADLIB_API void EA::Thread::SetThreadName(const char* pName) { SetThreadName(GetThreadId(), pName); }
  395. EATHREADLIB_API const char* EA::Thread::GetThreadName() { return GetThreadName(GetThreadId()); }
  396. EATHREADLIB_API void EA::Thread::SetThreadName(const EA::Thread::ThreadId& id, const char* pName)
  397. {
  398. EAThreadDynamicData* const pTDD = FindThreadDynamicData(id);
  399. if(pTDD)
  400. {
  401. if(pTDD->mName != pName) // self-assignment check
  402. {
  403. strncpy(pTDD->mName, pName, EATHREAD_NAME_SIZE);
  404. pTDD->mName[EATHREAD_NAME_SIZE - 1] = 0;
  405. }
  406. Internal::SetThreadName(pTDD);
  407. }
  408. }
  409. EATHREADLIB_API const char* EA::Thread::GetThreadName(const EA::Thread::ThreadId& id)
  410. {
  411. EAThreadDynamicData* const pTDD = FindThreadDynamicData(id);
  412. return pTDD ? pTDD->mName : "";
  413. }
  414. int EA::Thread::GetProcessorCount()
  415. {
  416. #if defined(EA_PLATFORM_WINDOWS)
  417. static int nProcessorCount = 0; // This doesn't really need to be an atomic integer.
  418. if(nProcessorCount == 0)
  419. {
  420. // A better function to use would possibly be KeQueryActiveProcessorCount
  421. // (NTKERNELAPI ULONG KeQueryActiveProcessorCount(PKAFFINITY ActiveProcessors))
  422. SYSTEM_INFO systemInfo;
  423. memset(&systemInfo, 0, sizeof(systemInfo));
  424. GetSystemInfo(&systemInfo);
  425. nProcessorCount = (int)systemInfo.dwNumberOfProcessors;
  426. }
  427. return nProcessorCount;
  428. #elif defined(EA_PLATFORM_OSX) || defined(EA_PLATFORM_BSD)
  429. // http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man3/sysctlbyname.3.html
  430. // We can use:
  431. // int sysctl(int* name, u_int namelen, void* oldp, size_t* oldlenp, void* newp, size_t newlen);
  432. // int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
  433. #ifdef EA_PLATFORM_BSD
  434. int mib[4] = { CTL_HW, HW_NCPU, 0, 0 };
  435. #else
  436. int mib[4] = { CTL_HW, HW_AVAILCPU, 0, 0 };
  437. #endif
  438. int cpuCount = 0; // Unfortunately, Apple's documentation fails to clarify if this needs to be 'int' or 'long'.
  439. size_t len = sizeof(cpuCount);
  440. sysctl(mib, 2, &cpuCount, &len, NULL, 0);
  441. if(cpuCount < 1)
  442. {
  443. mib[1] = HW_NCPU;
  444. sysctl(mib, 2, &cpuCount, &len, NULL, 0);
  445. if(cpuCount < 1)
  446. cpuCount = 1;
  447. }
  448. return cpuCount;
  449. // Maybe simpler, should try it out to make sure it works:
  450. //
  451. // int cpuCount = 0;
  452. // size_t len = sizeof(cpuCount);
  453. // if(sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0) != 0)
  454. // cpuCount = 1;
  455. // return cpuCount;
  456. #else
  457. // Posix doesn't provide a means to get this information.
  458. // Some Unixes provide sysconf() with the _SC_NPROCESSORS_ONLN or _SC_NPROCESSORS_CONF option.
  459. // Another option is to count the number of entries in /proc/cpuinfo
  460. #ifdef _SC_NPROCESSORS_ONLN
  461. return (int)sysconf(_SC_NPROCESSORS_ONLN);
  462. #else
  463. return 1;
  464. #endif
  465. #endif
  466. }
  467. #if defined(EA_PLATFORM_WINDOWS)
  468. extern "C" __declspec(dllimport) void __stdcall Sleep(unsigned long dwMilliseconds);
  469. #endif
  470. void EA::Thread::ThreadSleep(const ThreadTime& timeRelative)
  471. {
  472. #if defined(EA_PLATFORM_WINDOWS)
  473. // There is no nanosleep on Windows, but there is Sleep.
  474. if(timeRelative == kTimeoutImmediate)
  475. Sleep(0);
  476. else
  477. Sleep((unsigned)((timeRelative.tv_sec * 1000) + (((timeRelative.tv_nsec % 1000) * 1000000))));
  478. #else
  479. if(timeRelative == kTimeoutImmediate)
  480. {
  481. sched_yield();
  482. }
  483. else
  484. {
  485. #if defined(EA_HAVE_nanosleep_DECL)
  486. nanosleep(&timeRelative, 0);
  487. #else
  488. // What to do?
  489. #endif
  490. }
  491. #endif
  492. }
  493. namespace EA
  494. {
  495. namespace Thread
  496. {
  497. EAThreadDynamicData* FindThreadDynamicData(ThreadId threadId);
  498. }
  499. }
  500. void EA::Thread::ThreadEnd(intptr_t threadReturnValue)
  501. {
  502. EAThreadDynamicData* const pTDD = FindThreadDynamicData(GetThreadId());
  503. if(pTDD)
  504. {
  505. pTDD->mnStatus = Thread::kStatusEnded;
  506. pTDD->mnReturnValue = threadReturnValue;
  507. pTDD->mRunMutex.Unlock();
  508. pTDD->Release();
  509. }
  510. pthread_exit((void*)threadReturnValue);
  511. }
  512. #if defined(EA_PLATFORM_APPLE)
  513. EA::Thread::SysThreadId EA::Thread::GetSysThreadId(ThreadId id)
  514. {
  515. return pthread_mach_thread_np(id);
  516. }
  517. EA::Thread::SysThreadId EA::Thread::GetSysThreadId()
  518. {
  519. return pthread_mach_thread_np(pthread_self()); // There isn't a self-specific version of pthread_mach_thread_np.
  520. }
  521. #endif
  522. EA::Thread::ThreadTime EA::Thread::GetThreadTime()
  523. {
  524. #if defined(EA_PLATFORM_WINDOWS) && !defined(__CYGWIN__)
  525. // We use this code instead of GetTickCount or similar because pthreads under
  526. // Win32 uses the 'system file time' definition (e.g. GetSystemTimeAsFileTime())
  527. // for current time. The implementation here is just like that in the
  528. // pthreads-Win32 ptw32_timespec.c file.
  529. int64_t ft;
  530. ThreadTime threadTime;
  531. GetSystemTimeAsFileTime((FILETIME*)&ft); // nTime64 is in intervals of 100ns.
  532. #define PTW32_TIMESPEC_TO_FILETIME_OFFSET (((int64_t)27111902 << 32) + (int64_t)3577643008)
  533. threadTime.tv_sec = (int)((ft - PTW32_TIMESPEC_TO_FILETIME_OFFSET) / 10000000);
  534. threadTime.tv_nsec = (int)((ft - PTW32_TIMESPEC_TO_FILETIME_OFFSET - ((int64_t)threadTime.tv_sec * (int64_t)10000000)) * 100);
  535. return threadTime;
  536. // Alternative which will likely be slower:
  537. //#include <sys/timeb.h>
  538. //ThreadTime threadTime;
  539. //_timeb fTime; _ftime(&fTime);
  540. //threadTime.tv_sec = (long)fTime.time;
  541. //threadTime.tv_nsec = fTime.millitm * 1000000;
  542. //return threadTime;
  543. #else
  544. // For some systems we may need to use gettimeofday() instead of clock_gettime().
  545. #if defined(EA_PLATFORM_LINUX) || defined(__CYGWIN__) || (_POSIX_TIMERS > 0)
  546. ThreadTime threadTime;
  547. clock_gettime(CLOCK_REALTIME, &threadTime); // If you get a linker error about clock_getttime, you need to link librt.a (specify -lrt to the linker).
  548. return threadTime;
  549. #else
  550. timeval temp;
  551. gettimeofday(&temp, NULL);
  552. return ThreadTime((ThreadTime::seconds_t)temp.tv_sec, (ThreadTime::nseconds_t)temp.tv_usec * 1000);
  553. #endif
  554. #endif
  555. }
  556. void EA::Thread::SetAssertionFailureFunction(EA::Thread::AssertionFailureFunction pAssertionFailureFunction, void* pContext)
  557. {
  558. gpAssertionFailureFunction = pAssertionFailureFunction;
  559. gpAssertionFailureContext = pContext;
  560. }
  561. void EA::Thread::AssertionFailure(const char* pExpression)
  562. {
  563. if(gpAssertionFailureFunction)
  564. gpAssertionFailureFunction(pExpression, gpAssertionFailureContext);
  565. else
  566. {
  567. #if EAT_ASSERT_ENABLED
  568. #ifdef EA_PLATFORM_WINDOWS
  569. OutputDebugStringA("EA::Thread::AssertionFailure: ");
  570. OutputDebugStringA(pExpression);
  571. OutputDebugStringA("\n");
  572. #else
  573. printf("EA::Thread::AssertionFailure: ");
  574. printf("%s", pExpression);
  575. printf("\n");
  576. fflush(stdout);
  577. fflush(stderr);
  578. #endif
  579. EATHREAD_DEBUG_BREAK();
  580. #endif
  581. }
  582. }
  583. #endif // defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE