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