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 #include <sched.h> 35 36 #if MT_PLATFORM_OSX 37 #include <thread> 38 #endif 39 40 #include <sys/mman.h> 41 42 #ifndef MAP_ANONYMOUS 43 #define MAP_ANONYMOUS MAP_ANON 44 #endif 45 46 #ifndef MAP_STACK 47 #define MAP_STACK (0) 48 #endif 49 50 #include <Platform/Common/MTThread.h> 51 #include <MTAppInterop.h> 52 53 namespace MT 54 { 55 // 56 // Signals the calling thread to yield execution to another thread that is ready to run. 57 // 58 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 59 inline void YieldThread() 60 { 61 int err = sched_yield(); 62 MT_USED_IN_ASSERT(err); 63 MT_ASSERT(err == 0, "pthread_yield - error"); 64 } 65 66 67 class ThreadId 68 { 69 pthread_t id; 70 Atomic32<uint32> isInitialized; 71 72 void Assign(const ThreadId& other) 73 { 74 id = other.id; 75 isInitialized.Store(other.isInitialized.Load()); 76 } 77 78 public: 79 80 ThreadId() 81 { 82 isInitialized.Store(0); 83 } 84 85 mt_forceinline ThreadId(const ThreadId& other) 86 { 87 Assign(other); 88 } 89 90 mt_forceinline ThreadId& operator=(const ThreadId& other) 91 { 92 Assign(other); 93 return *this; 94 } 95 96 mt_forceinline static ThreadId Self() 97 { 98 ThreadId selfThread; 99 selfThread.id = pthread_self(); 100 selfThread.isInitialized.Store(1); 101 return selfThread; 102 } 103 104 mt_forceinline bool IsValid() const 105 { 106 return (isInitialized.Load() != 0); 107 } 108 109 mt_forceinline bool IsEqual(const ThreadId& other) 110 { 111 if (isInitialized.Load() != other.isInitialized.Load()) 112 { 113 return false; 114 } 115 if (pthread_equal(id, other.id) == false) 116 { 117 return false; 118 } 119 return true; 120 } 121 122 mt_forceinline uint64 AsUInt64() const 123 { 124 if (isInitialized.Load() == 0) 125 { 126 return (uint64)-1; 127 } 128 129 return (uint64)id; 130 } 131 }; 132 133 134 135 class Thread : public ThreadBase 136 { 137 pthread_t thread; 138 pthread_attr_t threadAttr; 139 140 Memory::StackDesc stackDesc; 141 142 size_t stackSize; 143 144 bool isStarted; 145 146 static void* ThreadFuncInternal(void* pThread) 147 { 148 Thread* self = (Thread *)pThread; 149 self->func(self->funcData); 150 return nullptr; 151 } 152 153 #if MT_PLATFORM_OSX 154 //TODO: support OSX priority and bind to processors 155 #else 156 static void GetAffinityMask(cpu_set_t & cpu_mask, uint32 cpuCore) 157 { 158 CPU_ZERO(&cpu_mask); 159 160 if (cpuCore == MT_CPUCORE_ANY) 161 { 162 uint32 threadsCount = (uint32)GetNumberOfHardwareThreads(); 163 for(uint32 i = 0; i < threadsCount; i++) 164 { 165 CPU_SET(i, &cpu_mask); 166 } 167 } else 168 { 169 CPU_SET(cpuCore, &cpu_mask); 170 } 171 } 172 173 174 static int GetPriority(ThreadPriority::Type priority) 175 { 176 int min_prio = sched_get_priority_min (SCHED_FIFO); 177 int max_prio = sched_get_priority_max (SCHED_FIFO); 178 int default_prio = (max_prio - min_prio) / 2; 179 180 switch(priority) 181 { 182 case ThreadPriority::DEFAULT: 183 return default_prio; 184 case ThreadPriority::HIGH: 185 return max_prio; 186 case ThreadPriority::LOW: 187 return min_prio; 188 default: 189 MT_REPORT_ASSERT("Invalid thread priority"); 190 } 191 192 return default_prio; 193 } 194 #endif 195 196 197 public: 198 199 Thread() 200 : stackSize(0) 201 , isStarted(false) 202 { 203 } 204 205 void* GetStackBottom() 206 { 207 return stackDesc.stackBottom; 208 } 209 210 size_t GetStackSize() 211 { 212 return stackSize; 213 } 214 215 216 void Start(size_t _stackSize, TThreadEntryPoint entryPoint, void* userData, uint32 cpuCore = MT_CPUCORE_ANY, ThreadPriority::Type priority = ThreadPriority::DEFAULT) 217 { 218 MT_ASSERT(!isStarted, "Thread already stared"); 219 220 MT_ASSERT(func == nullptr, "Thread already started"); 221 222 func = entryPoint; 223 funcData = userData; 224 225 stackDesc = Memory::AllocStack(_stackSize); 226 stackSize = stackDesc.GetStackSize(); 227 228 MT_ASSERT(stackSize >= PTHREAD_STACK_MIN, "Thread stack to small"); 229 230 int err = pthread_attr_init(&threadAttr); 231 MT_USED_IN_ASSERT(err); 232 MT_ASSERT(err == 0, "pthread_attr_init - error"); 233 234 err = pthread_attr_setstack(&threadAttr, stackDesc.stackBottom, stackSize); 235 MT_USED_IN_ASSERT(err); 236 MT_ASSERT(err == 0, "pthread_attr_setstack - error"); 237 238 err = pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_JOINABLE); 239 MT_USED_IN_ASSERT(err); 240 MT_ASSERT(err == 0, "pthread_attr_setdetachstate - error"); 241 242 #if MT_PLATFORM_OSX 243 MT_UNUSED(cpuCore); 244 MT_UNUSED(priority); 245 246 //TODO: support OSX priority and bind to processors 247 #else 248 err = pthread_attr_setinheritsched(&threadAttr, PTHREAD_EXPLICIT_SCHED); 249 MT_USED_IN_ASSERT(err); 250 MT_ASSERT(err == 0, "pthread_attr_setinheritsched - error"); 251 252 cpu_set_t cpu_mask; 253 GetAffinityMask(cpu_mask, cpuCore); 254 err = pthread_attr_setaffinity_np(&threadAttr, sizeof(cpu_mask), &cpu_mask); 255 MT_USED_IN_ASSERT(err); 256 MT_ASSERT(err == 0, "pthread_attr_setaffinity_np - error"); 257 258 struct sched_param params; 259 params.sched_priority = GetPriority(priority); 260 err = pthread_attr_setschedparam(&threadAttr, ¶ms); 261 MT_USED_IN_ASSERT(err); 262 MT_ASSERT(err == 0, "pthread_attr_setschedparam - error"); 263 #endif 264 265 isStarted = true; 266 267 err = pthread_create(&thread, &threadAttr, ThreadFuncInternal, this); 268 MT_USED_IN_ASSERT(err); 269 MT_ASSERT(err == 0, "pthread_create - error"); 270 } 271 272 void Join() 273 { 274 MT_ASSERT(isStarted, "Thread is not started"); 275 276 if (func == nullptr) 277 { 278 return; 279 } 280 281 void *threadStatus = nullptr; 282 int err = pthread_join(thread, &threadStatus); 283 MT_USED_IN_ASSERT(err); 284 MT_ASSERT(err == 0, "pthread_join - error"); 285 286 err = pthread_attr_destroy(&threadAttr); 287 MT_USED_IN_ASSERT(err); 288 MT_ASSERT(err == 0, "pthread_attr_destroy - error"); 289 290 func = nullptr; 291 funcData = nullptr; 292 293 if (stackDesc.stackMemory != nullptr) 294 { 295 Memory::FreeStack(stackDesc); 296 } 297 298 stackSize = 0; 299 isStarted = false; 300 } 301 302 303 static int GetNumberOfHardwareThreads() 304 { 305 #if MT_PLATFORM_OSX 306 return std::thread::hardware_concurrency(); 307 #else 308 long numberOfProcessors = sysconf( _SC_NPROCESSORS_ONLN ); 309 return (int)numberOfProcessors; 310 #endif 311 } 312 313 #ifdef MT_INSTRUMENTED_BUILD 314 static void SetThreadName(const char* threadName) 315 { 316 pthread_t callThread = pthread_self(); 317 pthread_setname_np(callThread, threadName); 318 } 319 #endif 320 321 static void SetThreadSchedulingPolicy(uint32 cpuCore, ThreadPriority::Type priority = ThreadPriority::DEFAULT) 322 { 323 #if MT_PLATFORM_OSX 324 MT_UNUSED(cpuCore); 325 MT_UNUSED(priority); 326 327 //TODO: support OSX priority and bind to processors 328 #else 329 pthread_t callThread = pthread_self(); 330 331 int sched_priority = GetPriority(priority); 332 int err = pthread_setschedprio(callThread, sched_priority); 333 MT_USED_IN_ASSERT(err); 334 MT_ASSERT(err == 0, "pthread_setschedprio - error"); 335 336 cpu_set_t cpu_mask; 337 GetAffinityMask(cpu_mask, cpuCore); 338 err = pthread_setaffinity_np(callThread, sizeof(cpu_mask), &cpu_mask); 339 MT_USED_IN_ASSERT(err); 340 MT_ASSERT(err == 0, "pthread_setaffinity_np - error"); 341 #endif 342 } 343 344 345 static void Sleep(uint32 milliseconds) 346 { 347 struct timespec req; 348 int sec = (int)(milliseconds / 1000); 349 milliseconds = milliseconds - (sec*1000); 350 req.tv_sec = sec; 351 req.tv_nsec = milliseconds * 1000000L; 352 while (nanosleep(&req,&req) == -1 ) 353 { 354 continue; 355 } 356 } 357 358 }; 359 360 361 } 362 363 364 #endif 365