#include #include "GLTB/exception.h" #include "GLTB/thread.h" #ifdef WIN32 #include #else #include #include #endif namespace gltb { #ifndef WIN32 unsigned int Thread::threadIDCounter=0; std::map Thread::threadIDMap; gltb::RefPtr Thread::threadIDMapLock=RWLock::createRWLock(); #endif #if defined(WIN32) && _MSC_VER < 1900 __declspec(thread) bool staticUniqueIdInitialized = false; __declspec(thread) int staticUniqueId; #else thread_local bool staticUniqueIdInitialized = false; thread_local int staticUniqueId; #endif Thread::Thread(Runnable *runnable) { this->runnable=runnable; } Thread::~Thread() { #ifdef WIN32 CloseHandle(threadHandle); #else #endif } void Thread::start() { #ifdef WIN32 threadHandle=CreateThread(NULL,0,runner,this,0,NULL); if(threadHandle==NULL) { throw Error("unable to create new thread","ThreadWin32::start()"); } #else if(pthread_create(&thread,NULL,runner,this)!=0) { throw Error("unable to create new thread","ThreadPThread::start()"); } #endif } void Thread::run() { threadID=getCurrentThreadUniqueID(); runnable->run(); } void Thread::join() { #ifdef WIN32 WaitForSingleObject(threadHandle,INFINITE); #else pthread_join(thread,NULL); #endif } void Thread::kill() { #ifdef WIN32 TerminateThread(threadHandle,0); #else pthread_kill(thread,SIGKILL); #endif } void Thread::assignToProcessor(int processor) { #if defined WIN32 // TODO implement this /* * This is amazingly messy on Windows: * * Before Windows 7 there was no way to support more than 64 * processors here. Starting with Win 7 there is an amazingly * frustrating interface for assigning threads to more than 64 * processors at the same time which is based on the notion of * "processor groups". */ // first we need to figure out if we run on old windows OSVERSIONINFO osVersionInfo; osVersionInfo.dwOSVersionInfoSize = sizeof(osVersionInfo); GetVersionEx(&osVersionInfo); bool isOldWindows = false; if(osVersionInfo.dwMajorVersion < 6 || (osVersionInfo.dwMajorVersion == 6 && osVersionInfo.dwMinorVersion < 1)) { isOldWindows = true; } if(isOldWindows) { // this is impossible on older Windows versions, don't even try if(processor >= 64) { return; } DWORD threadAffinityMask = 1 << processor; SetThreadAffinityMask(threadHandle, threadAffinityMask); SetThreadIdealProcessor(threadHandle, processor); } else { // need to get these functions via GetProcAddress as they only exist starting from Win7: HMODULE kernel = GetModuleHandleA("kernel32.dll"); auto GetActiveProcessorGroupCount = (WORD (*)()) GetProcAddress(kernel, "GetActiveProcessorGroupCount"); auto GetActiveProcessorCount = (DWORD (*)(WORD)) GetProcAddress(kernel, "GetActiveProcessorCount"); auto SetThreadGroupAffinity = (BOOL (*)(HANDLE, const GROUP_AFFINITY*, PGROUP_AFFINITY)) GetProcAddress(kernel, "SetThreadGroupAffinity"); auto SetThreadIdealProcessorEx = (BOOL (*)(HANDLE, PPROCESSOR_NUMBER, PPROCESSOR_NUMBER)) GetProcAddress(kernel, "SetThreadIdealProcessorEx"); // figure out the processor group that target processor belongs to int numGroups = GetActiveProcessorGroupCount(); int targetGroup; int targetProcessor; int numProcessors = 0; bool foundTarget = false; for(int i = 0; i < numGroups; i++) { numProcessors += GetActiveProcessorCount(i); if(numProcessors > processor) { targetGroup = i; targetProcessor = numProcessors - GetActiveProcessorCount(i) + processor; foundTarget = true; break; } } // processor number is probably too high if(!foundTarget) { return; } GROUP_AFFINITY groupAffinity; groupAffinity.Group = targetGroup; groupAffinity.Mask = 1 << targetProcessor; groupAffinity.Reserved[0] = 0; groupAffinity.Reserved[1] = 0; groupAffinity.Reserved[2] = 0; SetThreadGroupAffinity(threadHandle, &groupAffinity, NULL); PROCESSOR_NUMBER processorNumber; processorNumber.Number = targetProcessor; processorNumber.Group = targetGroup; processorNumber.Reserved = 0; SetThreadIdealProcessorEx(threadHandle, &processorNumber, NULL); } #elif defined LINUX cpu_set_t cpuSet; CPU_ZERO(&cpuSet); CPU_SET(processor, &cpuSet); pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuSet); #endif } unsigned int Thread::getUniqueID() { return threadID; } unsigned int Thread::getCurrentThreadUniqueID() { if(staticUniqueIdInitialized) { return staticUniqueId; } #ifdef WIN32 return GetCurrentThreadId(); #else // this is a hassle because pthread_t is an opaque type and the sole guaranteed unique ID pthread_t self=pthread_self(); threadIDMapLock->readLock(); auto iter=threadIDMap.find(self); unsigned int id; if(iter==threadIDMap.end()) { // we're on a thread without pre-assigned ID (like the main thread) // so generate an ID threadIDMapLock->readUnlock(); threadIDMapLock->writeLock(); id=atomicInc(&threadIDCounter); threadIDMap[self]=id; threadIDMapLock->writeUnlock(); } else { // return the ID that was already assigned id=iter->second; threadIDMapLock->readUnlock(); } staticUniqueIdInitialized = true; staticUniqueId = id; return id; #endif } unsigned int Thread::getCurrentProcessor() { #if defined(WIN32) return GetCurrentProcessorNumber(); #else return sched_getcpu(); #endif } #ifdef WIN32 DWORD WINAPI Thread::runner(LPVOID thread) #else void *Thread::runner(void *thread) #endif { try { ((Thread*)thread)->run(); return 0; } catch(Exception &e) { std::cerr << e.asFormattedText() << std::endl; } return 0; } Thread *Thread::createThread(Runnable *runnable) { return new Thread(runnable); } }