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