123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- // Copyright 2009-2021 Intel Corporation
- // SPDX-License-Identifier: Apache-2.0
- #include "thread.h"
- #include "sysinfo.h"
- #include "estring.h"
- #include <iostream>
- #if defined(__ARM_NEON)
- #include "../simd/arm/emulation.h"
- #else
- #include <xmmintrin.h>
- #if defined(__EMSCRIPTEN__)
- #include "../simd/wasm/emulation.h"
- #endif
- #endif
- #if defined(PTHREADS_WIN32)
- #pragma comment (lib, "pthreadVC.lib")
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- /// Windows Platform
- ////////////////////////////////////////////////////////////////////////////////
- #if defined(__WIN32__)
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- namespace embree
- {
- /*! set the affinity of a given thread */
- void setAffinity(HANDLE thread, ssize_t affinity)
- {
- typedef WORD (WINAPI *GetActiveProcessorGroupCountFunc)();
- typedef DWORD (WINAPI *GetActiveProcessorCountFunc)(WORD);
- typedef BOOL (WINAPI *SetThreadGroupAffinityFunc)(HANDLE, const GROUP_AFFINITY *, PGROUP_AFFINITY);
- typedef BOOL (WINAPI *SetThreadIdealProcessorExFunc)(HANDLE, PPROCESSOR_NUMBER, PPROCESSOR_NUMBER);
- HMODULE hlib = LoadLibrary("Kernel32");
- GetActiveProcessorGroupCountFunc pGetActiveProcessorGroupCount = (GetActiveProcessorGroupCountFunc)GetProcAddress(hlib, "GetActiveProcessorGroupCount");
- GetActiveProcessorCountFunc pGetActiveProcessorCount = (GetActiveProcessorCountFunc)GetProcAddress(hlib, "GetActiveProcessorCount");
- SetThreadGroupAffinityFunc pSetThreadGroupAffinity = (SetThreadGroupAffinityFunc)GetProcAddress(hlib, "SetThreadGroupAffinity");
- SetThreadIdealProcessorExFunc pSetThreadIdealProcessorEx = (SetThreadIdealProcessorExFunc)GetProcAddress(hlib, "SetThreadIdealProcessorEx");
- if (pGetActiveProcessorGroupCount && pGetActiveProcessorCount && pSetThreadGroupAffinity && pSetThreadIdealProcessorEx)
- {
- int groups = pGetActiveProcessorGroupCount();
- int totalProcessors = 0, group = 0, number = 0;
- for (int i = 0; i<groups; i++) {
- int processors = pGetActiveProcessorCount(i);
- if (totalProcessors + processors > affinity) {
- group = i;
- number = (int)affinity - totalProcessors;
- break;
- }
- totalProcessors += processors;
- }
-
- GROUP_AFFINITY groupAffinity;
- groupAffinity.Group = (WORD)group;
- groupAffinity.Mask = (KAFFINITY)(uint64_t(1) << number);
- groupAffinity.Reserved[0] = 0;
- groupAffinity.Reserved[1] = 0;
- groupAffinity.Reserved[2] = 0;
- if (!pSetThreadGroupAffinity(thread, &groupAffinity, nullptr))
- WARNING("SetThreadGroupAffinity failed"); // on purpose only a warning
-
- PROCESSOR_NUMBER processorNumber;
- processorNumber.Group = group;
- processorNumber.Number = number;
- processorNumber.Reserved = 0;
- if (!pSetThreadIdealProcessorEx(thread, &processorNumber, nullptr))
- WARNING("SetThreadIdealProcessorEx failed"); // on purpose only a warning
- }
- else
- {
- if (!SetThreadAffinityMask(thread, DWORD_PTR(uint64_t(1) << affinity)))
- WARNING("SetThreadAffinityMask failed"); // on purpose only a warning
- if (SetThreadIdealProcessor(thread, (DWORD)affinity) == (DWORD)-1)
- WARNING("SetThreadIdealProcessor failed"); // on purpose only a warning
- }
- }
- /*! set affinity of the calling thread */
- void setAffinity(ssize_t affinity) {
- setAffinity(GetCurrentThread(), affinity);
- }
- struct ThreadStartupData
- {
- public:
- ThreadStartupData (thread_func f, void* arg)
- : f(f), arg(arg) {}
- public:
- thread_func f;
- void* arg;
- };
- DWORD WINAPI threadStartup(LPVOID ptr)
- {
- ThreadStartupData* parg = (ThreadStartupData*) ptr;
- _mm_setcsr(_mm_getcsr() | /*FTZ:*/ (1<<15) | /*DAZ:*/ (1<<6));
- parg->f(parg->arg);
- delete parg;
- return 0;
- }
- #if !defined(PTHREADS_WIN32)
- /*! creates a hardware thread running on specific core */
- thread_t createThread(thread_func f, void* arg, size_t stack_size, ssize_t threadID)
- {
- HANDLE thread = CreateThread(nullptr, stack_size, threadStartup, new ThreadStartupData(f,arg), 0, nullptr);
- if (thread == nullptr) FATAL("CreateThread failed");
- if (threadID >= 0) setAffinity(thread, threadID);
- return thread_t(thread);
- }
- /*! the thread calling this function gets yielded */
- void yield() {
- SwitchToThread();
- }
- /*! waits until the given thread has terminated */
- void join(thread_t tid) {
- WaitForSingleObject(HANDLE(tid), INFINITE);
- CloseHandle(HANDLE(tid));
- }
- /*! destroy a hardware thread by its handle */
- void destroyThread(thread_t tid) {
- TerminateThread(HANDLE(tid),0);
- CloseHandle(HANDLE(tid));
- }
- /*! creates thread local storage */
- tls_t createTls() {
- return tls_t(size_t(TlsAlloc()));
- }
- /*! set the thread local storage pointer */
- void setTls(tls_t tls, void* const ptr) {
- TlsSetValue(DWORD(size_t(tls)), ptr);
- }
- /*! return the thread local storage pointer */
- void* getTls(tls_t tls) {
- return TlsGetValue(DWORD(size_t(tls)));
- }
- /*! destroys thread local storage identifier */
- void destroyTls(tls_t tls) {
- TlsFree(DWORD(size_t(tls)));
- }
- #endif
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- /// Linux Platform
- ////////////////////////////////////////////////////////////////////////////////
- #if defined(__LINUX__) && !defined(__ANDROID__)
- #include <fstream>
- #include <sstream>
- #include <algorithm>
- namespace embree
- {
- static MutexSys mutex;
- static std::vector<size_t> threadIDs;
-
- /* changes thread ID mapping such that we first fill up all thread on one core */
- size_t mapThreadID(size_t threadID)
- {
- Lock<MutexSys> lock(mutex);
-
- if (threadIDs.size() == 0)
- {
- /* parse thread/CPU topology */
- for (size_t cpuID=0;;cpuID++)
- {
- std::fstream fs;
- std::string cpu = std::string("/sys/devices/system/cpu/cpu") + std::to_string((long long)cpuID) + std::string("/topology/thread_siblings_list");
- fs.open (cpu.c_str(), std::fstream::in);
- if (fs.fail()) break;
- int i;
- while (fs >> i)
- {
- if (std::none_of(threadIDs.begin(),threadIDs.end(),[&] (int id) { return id == i; }))
- threadIDs.push_back(i);
- if (fs.peek() == ',')
- fs.ignore();
- }
- fs.close();
- }
- #if 0
- for (size_t i=0;i<threadIDs.size();i++)
- std::cout << i << " -> " << threadIDs[i] << std::endl;
- #endif
- /* verify the mapping and do not use it if the mapping has errors */
- for (size_t i=0;i<threadIDs.size();i++) {
- for (size_t j=0;j<threadIDs.size();j++) {
- if (i != j && threadIDs[i] == threadIDs[j]) {
- threadIDs.clear();
- }
- }
- }
- }
- /* re-map threadIDs if mapping is available */
- size_t ID = threadID;
- if (threadID < threadIDs.size())
- ID = threadIDs[threadID];
- /* find correct thread to affinitize to */
- cpu_set_t set;
- CPU_ZERO(&set);
-
- if (pthread_getaffinity_np(pthread_self(), sizeof(set), &set) == 0)
- {
- for (int i=0, j=0; i<CPU_SETSIZE; i++)
- {
- if (!CPU_ISSET(i,&set)) continue;
- if (j == ID) {
- ID = i;
- break;
- }
- j++;
- }
- }
- return ID;
- }
- /*! set affinity of the calling thread */
- void setAffinity(ssize_t affinity)
- {
- cpu_set_t cset;
- CPU_ZERO(&cset);
- //size_t threadID = mapThreadID(affinity); // this is not working properly in LXC containers when some processors are disabled
- size_t threadID = affinity;
- CPU_SET(threadID, &cset);
- pthread_setaffinity_np(pthread_self(), sizeof(cset), &cset);
- }
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- /// Android Platform
- ////////////////////////////////////////////////////////////////////////////////
- #if defined(__ANDROID__)
- namespace embree
- {
- /*! set affinity of the calling thread */
- void setAffinity(ssize_t affinity)
- {
- cpu_set_t cset;
- CPU_ZERO(&cset);
- CPU_SET(affinity, &cset);
- sched_setaffinity(0, sizeof(cset), &cset);
- }
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- /// FreeBSD Platform
- ////////////////////////////////////////////////////////////////////////////////
- #if defined(__FreeBSD__)
- #include <pthread_np.h>
- namespace embree
- {
- /*! set affinity of the calling thread */
- void setAffinity(ssize_t affinity)
- {
- cpuset_t cset;
- CPU_ZERO(&cset);
- CPU_SET(affinity, &cset);
- pthread_setaffinity_np(pthread_self(), sizeof(cset), &cset);
- }
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- /// WebAssembly Platform
- ////////////////////////////////////////////////////////////////////////////////
- #if defined(__EMSCRIPTEN__)
- namespace embree
- {
- /*! set affinity of the calling thread */
- void setAffinity(ssize_t affinity)
- {
- // Setting thread affinity is not supported in WASM.
- }
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- /// MacOSX Platform
- ////////////////////////////////////////////////////////////////////////////////
- #if defined(__MACOSX__)
- #include <mach/thread_act.h>
- #include <mach/thread_policy.h>
- #include <mach/mach_init.h>
- namespace embree
- {
- /*! set affinity of the calling thread */
- void setAffinity(ssize_t affinity)
- {
- #if !defined(__ARM_NEON) // affinity seems not supported on M1 chip
-
- thread_affinity_policy ap;
- ap.affinity_tag = affinity;
- if (thread_policy_set(mach_thread_self(),THREAD_AFFINITY_POLICY,(thread_policy_t)&ap,THREAD_AFFINITY_POLICY_COUNT) != KERN_SUCCESS)
- WARNING("setting thread affinity failed"); // on purpose only a warning
-
- #endif
- }
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- /// Unix Platform
- ////////////////////////////////////////////////////////////////////////////////
- #if defined(__UNIX__) || defined(PTHREADS_WIN32)
- #include <pthread.h>
- #include <sched.h>
- #if defined(__USE_NUMA__)
- #include <numa.h>
- #endif
- namespace embree
- {
- struct ThreadStartupData
- {
- public:
- ThreadStartupData (thread_func f, void* arg, int affinity)
- : f(f), arg(arg), affinity(affinity) {}
- public:
- thread_func f;
- void* arg;
- ssize_t affinity;
- };
-
- static void* threadStartup(ThreadStartupData* parg)
- {
- _mm_setcsr(_mm_getcsr() | /*FTZ:*/ (1<<15) | /*DAZ:*/ (1<<6));
-
- /*! Mac OS X does not support setting affinity at thread creation time */
- #if defined(__MACOSX__)
- if (parg->affinity >= 0)
- setAffinity(parg->affinity);
- #endif
- parg->f(parg->arg);
- delete parg;
- return nullptr;
- }
- /*! creates a hardware thread running on specific core */
- thread_t createThread(thread_func f, void* arg, size_t stack_size, ssize_t threadID)
- {
- /* set stack size */
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- if (stack_size > 0) pthread_attr_setstacksize (&attr, stack_size);
- /* create thread */
- pthread_t* tid = new pthread_t;
- if (pthread_create(tid,&attr,(void*(*)(void*))threadStartup,new ThreadStartupData(f,arg,threadID)) != 0) {
- pthread_attr_destroy(&attr);
- delete tid;
- FATAL("pthread_create failed");
- }
- pthread_attr_destroy(&attr);
- /* set affinity */
- #if defined(__LINUX__) && !defined(__ANDROID__)
- if (threadID >= 0) {
- cpu_set_t cset;
- CPU_ZERO(&cset);
- threadID = mapThreadID(threadID);
- CPU_SET(threadID, &cset);
- pthread_setaffinity_np(*tid, sizeof(cset), &cset);
- }
- #elif defined(__FreeBSD__)
- if (threadID >= 0) {
- cpuset_t cset;
- CPU_ZERO(&cset);
- CPU_SET(threadID, &cset);
- pthread_setaffinity_np(*tid, sizeof(cset), &cset);
- }
- #elif defined(__ANDROID__)
- if (threadID >= 0) {
- cpu_set_t cset;
- CPU_ZERO(&cset);
- CPU_SET(threadID, &cset);
- sched_setaffinity(pthread_gettid_np(*tid), sizeof(cset), &cset);
- }
- #endif
- return thread_t(tid);
- }
- /*! the thread calling this function gets yielded */
- void yield() {
- sched_yield();
- }
- /*! waits until the given thread has terminated */
- void join(thread_t tid) {
- if (pthread_join(*(pthread_t*)tid, nullptr) != 0)
- FATAL("pthread_join failed");
- delete (pthread_t*)tid;
- }
- /*! destroy a hardware thread by its handle */
- void destroyThread(thread_t tid) {
- #if defined(__ANDROID__)
- FATAL("Can't destroy threads on Android."); // pthread_cancel not implemented.
- #else
- pthread_cancel(*(pthread_t*)tid);
- delete (pthread_t*)tid;
- #endif
- }
- /*! creates thread local storage */
- tls_t createTls()
- {
- pthread_key_t* key = new pthread_key_t;
- if (pthread_key_create(key,nullptr) != 0) {
- delete key;
- FATAL("pthread_key_create failed");
- }
- return tls_t(key);
- }
- /*! return the thread local storage pointer */
- void* getTls(tls_t tls)
- {
- assert(tls);
- return pthread_getspecific(*(pthread_key_t*)tls);
- }
- /*! set the thread local storage pointer */
- void setTls(tls_t tls, void* const ptr)
- {
- assert(tls);
- if (pthread_setspecific(*(pthread_key_t*)tls, ptr) != 0)
- FATAL("pthread_setspecific failed");
- }
- /*! destroys thread local storage identifier */
- void destroyTls(tls_t tls)
- {
- assert(tls);
- if (pthread_key_delete(*(pthread_key_t*)tls) != 0)
- FATAL("pthread_key_delete failed");
- delete (pthread_key_t*)tls;
- }
- }
- #endif
|