1f25ce65dSSergey Makeev // The MIT License (MIT) 2f25ce65dSSergey Makeev // 3f25ce65dSSergey Makeev // Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev 4f25ce65dSSergey Makeev // 5f25ce65dSSergey Makeev // Permission is hereby granted, free of charge, to any person obtaining a copy 6f25ce65dSSergey Makeev // of this software and associated documentation files (the "Software"), to deal 7f25ce65dSSergey Makeev // in the Software without restriction, including without limitation the rights 8f25ce65dSSergey Makeev // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9f25ce65dSSergey Makeev // copies of the Software, and to permit persons to whom the Software is 10f25ce65dSSergey Makeev // furnished to do so, subject to the following conditions: 11f25ce65dSSergey Makeev // 12f25ce65dSSergey Makeev // The above copyright notice and this permission notice shall be included in 13f25ce65dSSergey Makeev // all copies or substantial portions of the Software. 14f25ce65dSSergey Makeev // 15f25ce65dSSergey Makeev // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16f25ce65dSSergey Makeev // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17f25ce65dSSergey Makeev // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18f25ce65dSSergey Makeev // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19f25ce65dSSergey Makeev // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20f25ce65dSSergey Makeev // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21f25ce65dSSergey Makeev // THE SOFTWARE. 22f25ce65dSSergey Makeev 2347d53e4dSSergey Makeev #pragma once 2447d53e4dSSergey Makeev 2581ec7369SSergey Makeev #ifndef __MT_THREAD__ 2681ec7369SSergey Makeev #define __MT_THREAD__ 2781ec7369SSergey Makeev 281e78cb24Ss.makeev_local #include <MTConfig.h> 2947d53e4dSSergey Makeev #include <pthread.h> 3047d53e4dSSergey Makeev #include <unistd.h> 3147d53e4dSSergey Makeev #include <time.h> 3247d53e4dSSergey Makeev #include <limits.h> 3347d53e4dSSergey Makeev #include <stdlib.h> 3400eacdeaSs.makeev_local #include <sched.h> 3547d53e4dSSergey Makeev 361e78cb24Ss.makeev_local #if MT_PLATFORM_OSX 37f27fe1eeSadmin #include <thread> 38f27fe1eeSadmin #endif 39f27fe1eeSadmin 40f27fe1eeSadmin #include <sys/mman.h> 41f27fe1eeSadmin 42f27fe1eeSadmin #ifndef MAP_ANONYMOUS 43f27fe1eeSadmin #define MAP_ANONYMOUS MAP_ANON 44f27fe1eeSadmin #endif 45f27fe1eeSadmin 46f27fe1eeSadmin #ifndef MAP_STACK 47f27fe1eeSadmin #define MAP_STACK (0) 48f27fe1eeSadmin #endif 49f27fe1eeSadmin 50603f1500SSergey Makeev #include <Platform/Common/MTThread.h> 51a565da4eSs.makeev_local #include <MTAppInterop.h> 5247d53e4dSSergey Makeev 5347d53e4dSSergey Makeev namespace MT 5447d53e4dSSergey Makeev { 55f7a9bfc3Ss.makeev_local // 56f7a9bfc3Ss.makeev_local // Signals the calling thread to yield execution to another thread that is ready to run. 57f7a9bfc3Ss.makeev_local // 58f7a9bfc3Ss.makeev_local //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// YieldThread()59f7a9bfc3Ss.makeev_local inline void YieldThread() 60f7a9bfc3Ss.makeev_local { 6100eacdeaSs.makeev_local int err = sched_yield(); 62f7a9bfc3Ss.makeev_local MT_USED_IN_ASSERT(err); 63f7a9bfc3Ss.makeev_local MT_ASSERT(err == 0, "pthread_yield - error"); 64f7a9bfc3Ss.makeev_local } 65f7a9bfc3Ss.makeev_local 669c716f68Ss.makeev_local 673d930776Ss.makeev_local class ThreadId 683d930776Ss.makeev_local { 69*47ecee31Ss.makeev_local protected: 703d930776Ss.makeev_local pthread_t id; 713d930776Ss.makeev_local Atomic32<uint32> isInitialized; 723d930776Ss.makeev_local Assign(const ThreadId & other)739c716f68Ss.makeev_local void Assign(const ThreadId& other) 749c716f68Ss.makeev_local { 759c716f68Ss.makeev_local id = other.id; 769c716f68Ss.makeev_local isInitialized.Store(other.isInitialized.Load()); 779c716f68Ss.makeev_local } 783d930776Ss.makeev_local 799c716f68Ss.makeev_local public: 803d930776Ss.makeev_local ThreadId()813d930776Ss.makeev_local ThreadId() 823d930776Ss.makeev_local { 833d930776Ss.makeev_local isInitialized.Store(0); 843d930776Ss.makeev_local } 853d930776Ss.makeev_local ThreadId(const ThreadId & other)86b2d53818Ss.makeev_local mt_forceinline ThreadId(const ThreadId& other) 873d930776Ss.makeev_local { 889c716f68Ss.makeev_local Assign(other); 893d930776Ss.makeev_local } 903d930776Ss.makeev_local 91b2d53818Ss.makeev_local mt_forceinline ThreadId& operator=(const ThreadId& other) 929c716f68Ss.makeev_local { 939c716f68Ss.makeev_local Assign(other); 949c716f68Ss.makeev_local return *this; 959c716f68Ss.makeev_local } 969c716f68Ss.makeev_local Self()97b2d53818Ss.makeev_local mt_forceinline static ThreadId Self() 989c716f68Ss.makeev_local { 999c716f68Ss.makeev_local ThreadId selfThread; 1009c716f68Ss.makeev_local selfThread.id = pthread_self(); 1019c716f68Ss.makeev_local selfThread.isInitialized.Store(1); 1029c716f68Ss.makeev_local return selfThread; 1039c716f68Ss.makeev_local } 1049c716f68Ss.makeev_local IsValid()105b2d53818Ss.makeev_local mt_forceinline bool IsValid() const 1069c716f68Ss.makeev_local { 1079c716f68Ss.makeev_local return (isInitialized.Load() != 0); 1089c716f68Ss.makeev_local } 1099c716f68Ss.makeev_local IsEqual(const ThreadId & other)110*47ecee31Ss.makeev_local mt_forceinline bool IsEqual(const ThreadId& other) const 1119c716f68Ss.makeev_local { 1129c716f68Ss.makeev_local if (isInitialized.Load() != other.isInitialized.Load()) 1139c716f68Ss.makeev_local { 1149c716f68Ss.makeev_local return false; 1159c716f68Ss.makeev_local } 1169c716f68Ss.makeev_local if (pthread_equal(id, other.id) == false) 1179c716f68Ss.makeev_local { 1189c716f68Ss.makeev_local return false; 1199c716f68Ss.makeev_local } 1209c716f68Ss.makeev_local return true; 1219c716f68Ss.makeev_local } 1229c716f68Ss.makeev_local AsUInt64()123b2d53818Ss.makeev_local mt_forceinline uint64 AsUInt64() const 1243d930776Ss.makeev_local { 1253d930776Ss.makeev_local if (isInitialized.Load() == 0) 1263d930776Ss.makeev_local { 1279c716f68Ss.makeev_local return (uint64)-1; 1283d930776Ss.makeev_local } 1293d930776Ss.makeev_local 1309c716f68Ss.makeev_local return (uint64)id; 1313d930776Ss.makeev_local } 1323d930776Ss.makeev_local }; 13347d53e4dSSergey Makeev 1349c716f68Ss.makeev_local 1359c716f68Ss.makeev_local 13647d53e4dSSergey Makeev class Thread : public ThreadBase 13747d53e4dSSergey Makeev { 13847d53e4dSSergey Makeev pthread_t thread; 13947d53e4dSSergey Makeev pthread_attr_t threadAttr; 14047d53e4dSSergey Makeev 141a6b7af00SSergey Makeev Memory::StackDesc stackDesc; 142a6b7af00SSergey Makeev 14347d53e4dSSergey Makeev size_t stackSize; 14447d53e4dSSergey Makeev 14547d53e4dSSergey Makeev bool isStarted; 14647d53e4dSSergey Makeev ThreadFuncInternal(void * pThread)14747d53e4dSSergey Makeev static void* ThreadFuncInternal(void* pThread) 14847d53e4dSSergey Makeev { 14947d53e4dSSergey Makeev Thread* self = (Thread *)pThread; 15047d53e4dSSergey Makeev self->func(self->funcData); 1513b52e8bcSSergey Makeev return nullptr; 15247d53e4dSSergey Makeev } 15347d53e4dSSergey Makeev 1543048619aSs.makeev_local #if MT_PLATFORM_OSX 1553048619aSs.makeev_local //TODO: support OSX priority and bind to processors 1563048619aSs.makeev_local #else GetAffinityMask(cpu_set_t & cpu_mask,uint32 cpuCore)157d7cf17b1Ss.makeev_local static void GetAffinityMask(cpu_set_t & cpu_mask, uint32 cpuCore) 158d7cf17b1Ss.makeev_local { 159d7cf17b1Ss.makeev_local CPU_ZERO(&cpu_mask); 160d7cf17b1Ss.makeev_local 161d7cf17b1Ss.makeev_local if (cpuCore == MT_CPUCORE_ANY) 162d7cf17b1Ss.makeev_local { 163d7cf17b1Ss.makeev_local uint32 threadsCount = (uint32)GetNumberOfHardwareThreads(); 164d7cf17b1Ss.makeev_local for(uint32 i = 0; i < threadsCount; i++) 165d7cf17b1Ss.makeev_local { 166d7cf17b1Ss.makeev_local CPU_SET(i, &cpu_mask); 167d7cf17b1Ss.makeev_local } 168d7cf17b1Ss.makeev_local } else 169d7cf17b1Ss.makeev_local { 170d7cf17b1Ss.makeev_local CPU_SET(cpuCore, &cpu_mask); 171d7cf17b1Ss.makeev_local } 172d7cf17b1Ss.makeev_local } 173d7cf17b1Ss.makeev_local 1743048619aSs.makeev_local GetPriority(ThreadPriority::Type priority)175d7cf17b1Ss.makeev_local static int GetPriority(ThreadPriority::Type priority) 176d7cf17b1Ss.makeev_local { 177d7cf17b1Ss.makeev_local int min_prio = sched_get_priority_min (SCHED_FIFO); 178d7cf17b1Ss.makeev_local int max_prio = sched_get_priority_max (SCHED_FIFO); 179d7cf17b1Ss.makeev_local int default_prio = (max_prio - min_prio) / 2; 180d7cf17b1Ss.makeev_local 181d7cf17b1Ss.makeev_local switch(priority) 182d7cf17b1Ss.makeev_local { 183d7cf17b1Ss.makeev_local case ThreadPriority::DEFAULT: 184d7cf17b1Ss.makeev_local return default_prio; 185d7cf17b1Ss.makeev_local case ThreadPriority::HIGH: 186d7cf17b1Ss.makeev_local return max_prio; 187d7cf17b1Ss.makeev_local case ThreadPriority::LOW: 188d7cf17b1Ss.makeev_local return min_prio; 189d7cf17b1Ss.makeev_local default: 190d7cf17b1Ss.makeev_local MT_REPORT_ASSERT("Invalid thread priority"); 191d7cf17b1Ss.makeev_local } 192d7cf17b1Ss.makeev_local 193d7cf17b1Ss.makeev_local return default_prio; 194d7cf17b1Ss.makeev_local } 1953048619aSs.makeev_local #endif 196d7cf17b1Ss.makeev_local 197d7cf17b1Ss.makeev_local 19847d53e4dSSergey Makeev public: 19947d53e4dSSergey Makeev Thread()20047d53e4dSSergey Makeev Thread() 201a6b7af00SSergey Makeev : stackSize(0) 20247d53e4dSSergey Makeev , isStarted(false) 20347d53e4dSSergey Makeev { 20447d53e4dSSergey Makeev } 20547d53e4dSSergey Makeev GetStackBottom()206f27fe1eeSadmin void* GetStackBottom() 20747d53e4dSSergey Makeev { 208a6b7af00SSergey Makeev return stackDesc.stackBottom; 20947d53e4dSSergey Makeev } 21047d53e4dSSergey Makeev GetStackSize()21147d53e4dSSergey Makeev size_t GetStackSize() 21247d53e4dSSergey Makeev { 21347d53e4dSSergey Makeev return stackSize; 21447d53e4dSSergey Makeev } 21547d53e4dSSergey Makeev 21647d53e4dSSergey Makeev 217d7cf17b1Ss.makeev_local void Start(size_t _stackSize, TThreadEntryPoint entryPoint, void* userData, uint32 cpuCore = MT_CPUCORE_ANY, ThreadPriority::Type priority = ThreadPriority::DEFAULT) 21847d53e4dSSergey Makeev { 21934a394c3SSergey Makeev MT_ASSERT(!isStarted, "Thread already stared"); 22047d53e4dSSergey Makeev 22134a394c3SSergey Makeev MT_ASSERT(func == nullptr, "Thread already started"); 22247d53e4dSSergey Makeev 22347d53e4dSSergey Makeev func = entryPoint; 22447d53e4dSSergey Makeev funcData = userData; 22547d53e4dSSergey Makeev 226a6b7af00SSergey Makeev stackDesc = Memory::AllocStack(_stackSize); 227a6b7af00SSergey Makeev stackSize = stackDesc.GetStackSize(); 22847d53e4dSSergey Makeev 22934a394c3SSergey Makeev MT_ASSERT(stackSize >= PTHREAD_STACK_MIN, "Thread stack to small"); 23047d53e4dSSergey Makeev 23147d53e4dSSergey Makeev int err = pthread_attr_init(&threadAttr); 2321d81bf1fSSergey Makeev MT_USED_IN_ASSERT(err); 23334a394c3SSergey Makeev MT_ASSERT(err == 0, "pthread_attr_init - error"); 23447d53e4dSSergey Makeev 235a6b7af00SSergey Makeev err = pthread_attr_setstack(&threadAttr, stackDesc.stackBottom, stackSize); 2361d81bf1fSSergey Makeev MT_USED_IN_ASSERT(err); 23734a394c3SSergey Makeev MT_ASSERT(err == 0, "pthread_attr_setstack - error"); 23847d53e4dSSergey Makeev 23947d53e4dSSergey Makeev err = pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_JOINABLE); 2401d81bf1fSSergey Makeev MT_USED_IN_ASSERT(err); 24134a394c3SSergey Makeev MT_ASSERT(err == 0, "pthread_attr_setdetachstate - error"); 24247d53e4dSSergey Makeev 243d7cf17b1Ss.makeev_local #if MT_PLATFORM_OSX 244d7cf17b1Ss.makeev_local MT_UNUSED(cpuCore); 245d7cf17b1Ss.makeev_local MT_UNUSED(priority); 246d7cf17b1Ss.makeev_local 247d7cf17b1Ss.makeev_local //TODO: support OSX priority and bind to processors 248d7cf17b1Ss.makeev_local #else 249d7cf17b1Ss.makeev_local err = pthread_attr_setinheritsched(&threadAttr, PTHREAD_EXPLICIT_SCHED); 250d7cf17b1Ss.makeev_local MT_USED_IN_ASSERT(err); 251d7cf17b1Ss.makeev_local MT_ASSERT(err == 0, "pthread_attr_setinheritsched - error"); 252d7cf17b1Ss.makeev_local 253d7cf17b1Ss.makeev_local cpu_set_t cpu_mask; 254d7cf17b1Ss.makeev_local GetAffinityMask(cpu_mask, cpuCore); 255d90c726aSs.makeev_local err = pthread_attr_setaffinity_np(&threadAttr, sizeof(cpu_mask), &cpu_mask); 256d7cf17b1Ss.makeev_local MT_USED_IN_ASSERT(err); 257d7cf17b1Ss.makeev_local MT_ASSERT(err == 0, "pthread_attr_setaffinity_np - error"); 258d7cf17b1Ss.makeev_local 259d7cf17b1Ss.makeev_local struct sched_param params; 260d7cf17b1Ss.makeev_local params.sched_priority = GetPriority(priority); 261d7cf17b1Ss.makeev_local err = pthread_attr_setschedparam(&threadAttr, ¶ms); 262d7cf17b1Ss.makeev_local MT_USED_IN_ASSERT(err); 263d7cf17b1Ss.makeev_local MT_ASSERT(err == 0, "pthread_attr_setschedparam - error"); 264d7cf17b1Ss.makeev_local #endif 265d7cf17b1Ss.makeev_local 26670c02537SSergey Makeev isStarted = true; 26770c02537SSergey Makeev 26847d53e4dSSergey Makeev err = pthread_create(&thread, &threadAttr, ThreadFuncInternal, this); 2691d81bf1fSSergey Makeev MT_USED_IN_ASSERT(err); 27034a394c3SSergey Makeev MT_ASSERT(err == 0, "pthread_create - error"); 27147d53e4dSSergey Makeev } 27247d53e4dSSergey Makeev Join()273c7362320Ss.makeev_local void Join() 27447d53e4dSSergey Makeev { 27534a394c3SSergey Makeev MT_ASSERT(isStarted, "Thread is not started"); 27647d53e4dSSergey Makeev 27747d53e4dSSergey Makeev if (func == nullptr) 27847d53e4dSSergey Makeev { 27947d53e4dSSergey Makeev return; 28047d53e4dSSergey Makeev } 28147d53e4dSSergey Makeev 28247d53e4dSSergey Makeev void *threadStatus = nullptr; 28347d53e4dSSergey Makeev int err = pthread_join(thread, &threadStatus); 2841d81bf1fSSergey Makeev MT_USED_IN_ASSERT(err); 28534a394c3SSergey Makeev MT_ASSERT(err == 0, "pthread_join - error"); 28647d53e4dSSergey Makeev 28747d53e4dSSergey Makeev err = pthread_attr_destroy(&threadAttr); 2881d81bf1fSSergey Makeev MT_USED_IN_ASSERT(err); 28934a394c3SSergey Makeev MT_ASSERT(err == 0, "pthread_attr_destroy - error"); 29047d53e4dSSergey Makeev 29147d53e4dSSergey Makeev func = nullptr; 29247d53e4dSSergey Makeev funcData = nullptr; 29347d53e4dSSergey Makeev 294a6b7af00SSergey Makeev if (stackDesc.stackMemory != nullptr) 29547d53e4dSSergey Makeev { 296a6b7af00SSergey Makeev Memory::FreeStack(stackDesc); 29747d53e4dSSergey Makeev } 29847d53e4dSSergey Makeev 299a6b7af00SSergey Makeev stackSize = 0; 30047d53e4dSSergey Makeev isStarted = false; 30147d53e4dSSergey Makeev } 30247d53e4dSSergey Makeev 30347d53e4dSSergey Makeev GetNumberOfHardwareThreads()30447d53e4dSSergey Makeev static int GetNumberOfHardwareThreads() 30547d53e4dSSergey Makeev { 3061e78cb24Ss.makeev_local #if MT_PLATFORM_OSX 307f27fe1eeSadmin return std::thread::hardware_concurrency(); 308f27fe1eeSadmin #else 30947d53e4dSSergey Makeev long numberOfProcessors = sysconf( _SC_NPROCESSORS_ONLN ); 31047d53e4dSSergey Makeev return (int)numberOfProcessors; 311f27fe1eeSadmin #endif 31247d53e4dSSergey Makeev } 31347d53e4dSSergey Makeev 314c88507a8Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD SetThreadName(const char * threadName)315d7cf17b1Ss.makeev_local static void SetThreadName(const char* threadName) 316d7cf17b1Ss.makeev_local { 317c88507a8Ss.makeev_local pthread_t callThread = pthread_self(); 318c88507a8Ss.makeev_local pthread_setname_np(callThread, threadName); 319d7cf17b1Ss.makeev_local } 320d7cf17b1Ss.makeev_local #endif 321d7cf17b1Ss.makeev_local 322d7cf17b1Ss.makeev_local static void SetThreadSchedulingPolicy(uint32 cpuCore, ThreadPriority::Type priority = ThreadPriority::DEFAULT) 323d7cf17b1Ss.makeev_local { 324d7cf17b1Ss.makeev_local #if MT_PLATFORM_OSX 325d7cf17b1Ss.makeev_local MT_UNUSED(cpuCore); 326d7cf17b1Ss.makeev_local MT_UNUSED(priority); 327d7cf17b1Ss.makeev_local 328d7cf17b1Ss.makeev_local //TODO: support OSX priority and bind to processors 329d7cf17b1Ss.makeev_local #else 330d7cf17b1Ss.makeev_local pthread_t callThread = pthread_self(); 331d7cf17b1Ss.makeev_local 332d7cf17b1Ss.makeev_local int sched_priority = GetPriority(priority); 333ee5db384Ss.makeev_local int err = pthread_setschedprio(callThread, sched_priority); 334d7cf17b1Ss.makeev_local MT_USED_IN_ASSERT(err); 335ee5db384Ss.makeev_local MT_ASSERT(err == 0, "pthread_setschedprio - error"); 336d7cf17b1Ss.makeev_local 337d7cf17b1Ss.makeev_local cpu_set_t cpu_mask; 338d7cf17b1Ss.makeev_local GetAffinityMask(cpu_mask, cpuCore); 339ee5db384Ss.makeev_local err = pthread_setaffinity_np(callThread, sizeof(cpu_mask), &cpu_mask); 340d7cf17b1Ss.makeev_local MT_USED_IN_ASSERT(err); 341d7cf17b1Ss.makeev_local MT_ASSERT(err == 0, "pthread_setaffinity_np - error"); 342c88507a8Ss.makeev_local #endif 343c88507a8Ss.makeev_local } 344c88507a8Ss.makeev_local 345c88507a8Ss.makeev_local Sleep(uint32 milliseconds)34647d53e4dSSergey Makeev static void Sleep(uint32 milliseconds) 34747d53e4dSSergey Makeev { 3482174fd67SSergey Makeev struct timespec req; 349bc48b7efSSergey Makeev int sec = (int)(milliseconds / 1000); 35047d53e4dSSergey Makeev milliseconds = milliseconds - (sec*1000); 35147d53e4dSSergey Makeev req.tv_sec = sec; 35247d53e4dSSergey Makeev req.tv_nsec = milliseconds * 1000000L; 35347d53e4dSSergey Makeev while (nanosleep(&req,&req) == -1 ) 35447d53e4dSSergey Makeev { 35547d53e4dSSergey Makeev continue; 35647d53e4dSSergey Makeev } 35747d53e4dSSergey Makeev } 35847d53e4dSSergey Makeev 35947d53e4dSSergey Makeev }; 36047d53e4dSSergey Makeev 36147d53e4dSSergey Makeev 36247d53e4dSSergey Makeev } 36347d53e4dSSergey Makeev 36447d53e4dSSergey Makeev 36581ec7369SSergey Makeev #endif 366