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