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