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 #pragma once 23 24 #ifndef __MT_THREAD__ 25 #define __MT_THREAD__ 26 27 #include <Platform/Common/MTThread.h> 28 29 namespace MT 30 { 31 // 32 // Signals the calling thread to yield execution to another thread that is ready to run. 33 // 34 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 35 inline void YieldThread() 36 { 37 ::SwitchToThread (); 38 } 39 40 41 class ThreadId 42 { 43 MW_DWORD id; 44 Atomic32<uint32> isInitialized; 45 46 void Assign(const ThreadId& other) 47 { 48 id = other.id; 49 isInitialized.Store(other.isInitialized.Load()); 50 } 51 52 public: 53 54 ThreadId() 55 { 56 isInitialized.Store(0); 57 } 58 59 mt_forceinline ThreadId(const ThreadId& other) 60 { 61 Assign(other); 62 } 63 64 mt_forceinline ThreadId& operator=(const ThreadId& other) 65 { 66 Assign(other); 67 return *this; 68 } 69 70 mt_forceinline static ThreadId Self() 71 { 72 ThreadId selfThread; 73 selfThread.id = ::GetCurrentThreadId(); 74 selfThread.isInitialized.Store(1); 75 return selfThread; 76 } 77 78 mt_forceinline bool IsValid() const 79 { 80 return (isInitialized.Load() != 0); 81 } 82 83 mt_forceinline bool IsEqual(const ThreadId& other) 84 { 85 if (isInitialized.Load() != other.isInitialized.Load()) 86 { 87 return false; 88 } 89 if (id != other.id) 90 { 91 return false; 92 } 93 return true; 94 } 95 96 mt_forceinline uint64 AsUInt64() const 97 { 98 if (isInitialized.Load() == 0) 99 { 100 return (uint64)-1; 101 } 102 103 return (uint64)id; 104 } 105 106 }; 107 108 109 class Thread : public ThreadBase 110 { 111 MW_HANDLE thread; 112 113 static MW_DWORD __stdcall ThreadFuncInternal(void *pThread) 114 { 115 Thread* self = (Thread*)pThread; 116 self->func(self->funcData); 117 return 0; 118 } 119 120 121 122 static int GetPriority(ThreadPriority::Type priority) 123 { 124 switch(priority) 125 { 126 case ThreadPriority::DEFAULT: 127 return MW_THREAD_PRIORITY_HIGHEST; 128 case ThreadPriority::HIGH: 129 return MW_THREAD_PRIORITY_NORMAL; 130 case ThreadPriority::LOW: 131 return MW_THREAD_PRIORITY_LOWEST; 132 default: 133 MT_REPORT_ASSERT("Invalid thread priority"); 134 } 135 136 return MW_THREAD_PRIORITY_NORMAL; 137 } 138 139 140 public: 141 142 Thread() 143 : thread(nullptr) 144 { 145 } 146 147 ~Thread() 148 { 149 MT_ASSERT(thread == nullptr, "Thread is not stopped!"); 150 } 151 152 void Start(size_t stackSize, TThreadEntryPoint entryPoint, void *userData, uint32 cpuCore = MT_CPUCORE_ANY, ThreadPriority::Type priority = ThreadPriority::DEFAULT) 153 { 154 MT_ASSERT(thread == nullptr, "Thread already started"); 155 156 func = entryPoint; 157 funcData = userData; 158 thread = ::CreateThread( nullptr, stackSize, ThreadFuncInternal, this, MW_CREATE_SUSPENDED, nullptr ); 159 MT_ASSERT(thread != nullptr, "Can't create thread"); 160 161 if (cpuCore == MT_CPUCORE_ANY) 162 { 163 cpuCore = MW_MAXIMUM_PROCESSORS; 164 } 165 MT_VERIFY((cpuCore >= 0 && cpuCore < (uint32)GetNumberOfHardwareThreads()) || cpuCore == MW_MAXIMUM_PROCESSORS, "Invalid cpu core specified", cpuCore=MW_MAXIMUM_PROCESSORS); 166 MW_DWORD res = ::SetThreadIdealProcessor(thread, cpuCore); 167 MT_USED_IN_ASSERT(res); 168 MT_ASSERT(res != (MW_DWORD)-1, "SetThreadIdealProcessor failed!"); 169 170 int sched_priority = GetPriority(priority); 171 172 MW_BOOL result = ::SetThreadPriority(thread, sched_priority); 173 MT_USED_IN_ASSERT(result); 174 MT_ASSERT(result != 0, "SetThreadPriority failed!"); 175 176 res = ::ResumeThread(thread); 177 MT_USED_IN_ASSERT(res); 178 MT_ASSERT(res != (MW_DWORD)-1, "ResumeThread failed!"); 179 } 180 181 void Join() 182 { 183 if (thread == nullptr) 184 { 185 return; 186 } 187 188 ::WaitForSingleObject(thread, MW_INFINITE); 189 MW_BOOL res = CloseHandle(thread); 190 MT_USED_IN_ASSERT(res); 191 MT_ASSERT(res != 0, "Can't close thread handle"); 192 thread = nullptr; 193 } 194 195 #ifdef MT_INSTRUMENTED_BUILD 196 static void SetThreadName(const char* threadName) 197 { 198 const int MW_EXCEPTION_EXECUTE_HANDLER = 1; 199 const MW_DWORD MW_MSVC_EXCEPTION = 0x406D1388; 200 201 #pragma pack(push,8) 202 typedef struct tagTHREADNAME_INFO 203 { 204 MW_DWORD dwType; // Must be 0x1000. 205 const char* szName; // Pointer to name (in user addr space). 206 MW_DWORD dwThreadID; // Thread ID (-1=caller thread). 207 MW_DWORD dwFlags; // Reserved for future use, must be zero. 208 } THREADNAME_INFO; 209 #pragma pack(pop) 210 211 THREADNAME_INFO info; 212 info.dwType = 0x1000; 213 info.szName = threadName; 214 info.dwThreadID = ::GetCurrentThreadId(); 215 info.dwFlags = 0; 216 217 __try 218 { 219 RaiseException(MW_MSVC_EXCEPTION, 0, sizeof(info) / sizeof(void*), (MW_ULONG_PTR*)&info); 220 } 221 __except (MW_EXCEPTION_EXECUTE_HANDLER) 222 { 223 } 224 } 225 #endif 226 227 static void SetThreadSchedulingPolicy(uint32 cpuCore, ThreadPriority::Type priority = ThreadPriority::DEFAULT) 228 { 229 if (cpuCore == MT_CPUCORE_ANY) 230 { 231 cpuCore = MW_MAXIMUM_PROCESSORS; 232 } 233 MT_VERIFY((cpuCore >= 0 && cpuCore < (uint32)GetNumberOfHardwareThreads()) || cpuCore == MW_MAXIMUM_PROCESSORS, "Invalid cpu core specified", cpuCore=MW_MAXIMUM_PROCESSORS); 234 MW_DWORD res = ::SetThreadIdealProcessor( ::GetCurrentThread(), cpuCore); 235 MT_USED_IN_ASSERT(res); 236 MT_ASSERT(res != (MW_DWORD)-1, "SetThreadIdealProcessor failed!"); 237 238 int sched_priority = GetPriority(priority); 239 240 MW_BOOL result = ::SetThreadPriority( ::GetCurrentThread(), sched_priority ); 241 MT_USED_IN_ASSERT(result); 242 MT_ASSERT(result != 0, "SetThreadPriority failed!"); 243 } 244 245 static int GetNumberOfHardwareThreads() 246 { 247 MW_SYSTEM_INFO sysinfo; 248 ::GetSystemInfo( &sysinfo ); 249 return sysinfo.dwNumberOfProcessors; 250 } 251 252 static void Sleep(uint32 milliseconds) 253 { 254 ::Sleep(milliseconds); 255 } 256 }; 257 258 259 } 260 261 262 #endif 263