eathread_unix.cpp 23 KB

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