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