1 // The MIT License (MIT) 2 // 3 // Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 #pragma once 24 25 #ifndef __MT_THREAD__ 26 #define __MT_THREAD__ 27 28 #include <MTConfig.h> 29 #include <pthread.h> 30 #include <unistd.h> 31 #include <time.h> 32 #include <limits.h> 33 #include <stdlib.h> 34 35 #if MT_PLATFORM_OSX 36 #include <thread> 37 #endif 38 39 #include <sys/mman.h> 40 41 #ifndef MAP_ANONYMOUS 42 #define MAP_ANONYMOUS MAP_ANON 43 #endif 44 45 #ifndef MAP_STACK 46 #define MAP_STACK (0) 47 #endif 48 49 #include <Platform/Common/MTThread.h> 50 #include <MTAppInterop.h> 51 52 namespace MT 53 { 54 // 55 // Signals the calling thread to yield execution to another thread that is ready to run. 56 // 57 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 58 inline void YieldThread() 59 { 60 int err = pthread_yield(); 61 MT_USED_IN_ASSERT(err); 62 MT_ASSERT(err == 0, "pthread_yield - error"); 63 } 64 65 class ThreadId 66 { 67 pthread_t id; 68 Atomic32<uint32> isInitialized; 69 70 public: 71 72 MT_NOCOPYABLE(ThreadId); 73 74 ThreadId() 75 { 76 isInitialized.Store(0); 77 } 78 79 void SetAsCurrentThread() 80 { 81 id = pthread_self(); 82 isInitialized.Store(1); 83 } 84 85 bool IsCurrentThread() const 86 { 87 if (isInitialized.Load() == 0) 88 { 89 return false; 90 } 91 92 pthread_t callThread = pthread_self(); 93 if (pthread_equal(callThread, id)) 94 { 95 return true; 96 } 97 return false; 98 } 99 }; 100 101 class Thread : public ThreadBase 102 { 103 pthread_t thread; 104 pthread_attr_t threadAttr; 105 106 Memory::StackDesc stackDesc; 107 108 size_t stackSize; 109 110 bool isStarted; 111 112 static void* ThreadFuncInternal(void* pThread) 113 { 114 Thread* self = (Thread *)pThread; 115 self->func(self->funcData); 116 return nullptr; 117 } 118 119 #if MT_PLATFORM_OSX 120 //TODO: support OSX priority and bind to processors 121 #else 122 static void GetAffinityMask(cpu_set_t & cpu_mask, uint32 cpuCore) 123 { 124 CPU_ZERO(&cpu_mask); 125 126 if (cpuCore == MT_CPUCORE_ANY) 127 { 128 uint32 threadsCount = (uint32)GetNumberOfHardwareThreads(); 129 for(uint32 i = 0; i < threadsCount; i++) 130 { 131 CPU_SET(i, &cpu_mask); 132 } 133 } else 134 { 135 CPU_SET(cpuCore, &cpu_mask); 136 } 137 } 138 139 140 static int GetPriority(ThreadPriority::Type priority) 141 { 142 int min_prio = sched_get_priority_min (SCHED_FIFO); 143 int max_prio = sched_get_priority_max (SCHED_FIFO); 144 int default_prio = (max_prio - min_prio) / 2; 145 146 switch(priority) 147 { 148 case ThreadPriority::DEFAULT: 149 return default_prio; 150 case ThreadPriority::HIGH: 151 return max_prio; 152 case ThreadPriority::LOW: 153 return min_prio; 154 default: 155 MT_REPORT_ASSERT("Invalid thread priority"); 156 } 157 158 return default_prio; 159 } 160 #endif 161 162 163 public: 164 165 Thread() 166 : stackSize(0) 167 , isStarted(false) 168 { 169 } 170 171 void* GetStackBottom() 172 { 173 return stackDesc.stackBottom; 174 } 175 176 size_t GetStackSize() 177 { 178 return stackSize; 179 } 180 181 182 void Start(size_t _stackSize, TThreadEntryPoint entryPoint, void* userData, uint32 cpuCore = MT_CPUCORE_ANY, ThreadPriority::Type priority = ThreadPriority::DEFAULT) 183 { 184 MT_ASSERT(!isStarted, "Thread already stared"); 185 186 MT_ASSERT(func == nullptr, "Thread already started"); 187 188 func = entryPoint; 189 funcData = userData; 190 191 stackDesc = Memory::AllocStack(_stackSize); 192 stackSize = stackDesc.GetStackSize(); 193 194 MT_ASSERT(stackSize >= PTHREAD_STACK_MIN, "Thread stack to small"); 195 196 int err = pthread_attr_init(&threadAttr); 197 MT_USED_IN_ASSERT(err); 198 MT_ASSERT(err == 0, "pthread_attr_init - error"); 199 200 err = pthread_attr_setstack(&threadAttr, stackDesc.stackBottom, stackSize); 201 MT_USED_IN_ASSERT(err); 202 MT_ASSERT(err == 0, "pthread_attr_setstack - error"); 203 204 err = pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_JOINABLE); 205 MT_USED_IN_ASSERT(err); 206 MT_ASSERT(err == 0, "pthread_attr_setdetachstate - error"); 207 208 #if MT_PLATFORM_OSX 209 MT_UNUSED(cpuCore); 210 MT_UNUSED(priority); 211 212 //TODO: support OSX priority and bind to processors 213 #else 214 err = pthread_attr_setinheritsched(&threadAttr, PTHREAD_EXPLICIT_SCHED); 215 MT_USED_IN_ASSERT(err); 216 MT_ASSERT(err == 0, "pthread_attr_setinheritsched - error"); 217 218 cpu_set_t cpu_mask; 219 GetAffinityMask(cpu_mask, cpuCore); 220 err = pthread_attr_setaffinity_np(&threadAttr, sizeof(cpu_mask), &cpu_mask); 221 MT_USED_IN_ASSERT(err); 222 MT_ASSERT(err == 0, "pthread_attr_setaffinity_np - error"); 223 224 struct sched_param params; 225 params.sched_priority = GetPriority(priority); 226 err = pthread_attr_setschedparam(&threadAttr, ¶ms); 227 MT_USED_IN_ASSERT(err); 228 MT_ASSERT(err == 0, "pthread_attr_setschedparam - error"); 229 #endif 230 231 isStarted = true; 232 233 err = pthread_create(&thread, &threadAttr, ThreadFuncInternal, this); 234 MT_USED_IN_ASSERT(err); 235 MT_ASSERT(err == 0, "pthread_create - error"); 236 } 237 238 void Join() 239 { 240 MT_ASSERT(isStarted, "Thread is not started"); 241 242 if (func == nullptr) 243 { 244 return; 245 } 246 247 void *threadStatus = nullptr; 248 int err = pthread_join(thread, &threadStatus); 249 MT_USED_IN_ASSERT(err); 250 MT_ASSERT(err == 0, "pthread_join - error"); 251 252 err = pthread_attr_destroy(&threadAttr); 253 MT_USED_IN_ASSERT(err); 254 MT_ASSERT(err == 0, "pthread_attr_destroy - error"); 255 256 func = nullptr; 257 funcData = nullptr; 258 259 if (stackDesc.stackMemory != nullptr) 260 { 261 Memory::FreeStack(stackDesc); 262 } 263 264 stackSize = 0; 265 isStarted = false; 266 } 267 268 269 static int GetNumberOfHardwareThreads() 270 { 271 #if MT_PLATFORM_OSX 272 return std::thread::hardware_concurrency(); 273 #else 274 long numberOfProcessors = sysconf( _SC_NPROCESSORS_ONLN ); 275 return (int)numberOfProcessors; 276 #endif 277 } 278 279 #ifdef MT_INSTRUMENTED_BUILD 280 static void SetThreadName(const char* threadName) 281 { 282 pthread_t callThread = pthread_self(); 283 pthread_setname_np(callThread, threadName); 284 } 285 #endif 286 287 static void SetThreadSchedulingPolicy(uint32 cpuCore, ThreadPriority::Type priority = ThreadPriority::DEFAULT) 288 { 289 #if MT_PLATFORM_OSX 290 MT_UNUSED(cpuCore); 291 MT_UNUSED(priority); 292 293 //TODO: support OSX priority and bind to processors 294 #else 295 pthread_t callThread = pthread_self(); 296 297 int sched_priority = GetPriority(priority); 298 int err = pthread_setschedprio(callThread, sched_priority); 299 MT_USED_IN_ASSERT(err); 300 MT_ASSERT(err == 0, "pthread_setschedprio - error"); 301 302 cpu_set_t cpu_mask; 303 GetAffinityMask(cpu_mask, cpuCore); 304 err = pthread_setaffinity_np(callThread, sizeof(cpu_mask), &cpu_mask); 305 MT_USED_IN_ASSERT(err); 306 MT_ASSERT(err == 0, "pthread_setaffinity_np - error"); 307 #endif 308 } 309 310 311 static void Sleep(uint32 milliseconds) 312 { 313 struct timespec req; 314 time_t sec = (int)(milliseconds/1000); 315 milliseconds = milliseconds - (sec*1000); 316 req.tv_sec = sec; 317 req.tv_nsec = milliseconds * 1000000L; 318 while (nanosleep(&req,&req) == -1 ) 319 { 320 continue; 321 } 322 } 323 324 }; 325 326 327 } 328 329 330 #endif 331