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