1f25ce65dSSergey Makeev // The MIT License (MIT) 2f25ce65dSSergey Makeev // 3f25ce65dSSergey Makeev // Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev 4f25ce65dSSergey Makeev // 5f25ce65dSSergey Makeev // Permission is hereby granted, free of charge, to any person obtaining a copy 6f25ce65dSSergey Makeev // of this software and associated documentation files (the "Software"), to deal 7f25ce65dSSergey Makeev // in the Software without restriction, including without limitation the rights 8f25ce65dSSergey Makeev // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9f25ce65dSSergey Makeev // copies of the Software, and to permit persons to whom the Software is 10f25ce65dSSergey Makeev // furnished to do so, subject to the following conditions: 11f25ce65dSSergey Makeev // 12f25ce65dSSergey Makeev // The above copyright notice and this permission notice shall be included in 13f25ce65dSSergey Makeev // all copies or substantial portions of the Software. 14f25ce65dSSergey Makeev // 15f25ce65dSSergey Makeev // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16f25ce65dSSergey Makeev // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17f25ce65dSSergey Makeev // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18f25ce65dSSergey Makeev // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19f25ce65dSSergey Makeev // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20f25ce65dSSergey Makeev // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21f25ce65dSSergey Makeev // THE SOFTWARE. 2247d53e4dSSergey Makeev #pragma once 23f25ce65dSSergey Makeev 2481ec7369SSergey Makeev #ifndef __MT_THREAD__ 2581ec7369SSergey Makeev #define __MT_THREAD__ 2681ec7369SSergey Makeev 2747d53e4dSSergey Makeev #include <Platform/Common/MTThread.h> 2847d53e4dSSergey Makeev 2947d53e4dSSergey Makeev namespace MT 3047d53e4dSSergey Makeev { 31f7a9bfc3Ss.makeev_local // 32f7a9bfc3Ss.makeev_local // Signals the calling thread to yield execution to another thread that is ready to run. 33f7a9bfc3Ss.makeev_local // 34f7a9bfc3Ss.makeev_local //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// YieldThread()35f7a9bfc3Ss.makeev_local inline void YieldThread() 36f7a9bfc3Ss.makeev_local { 37f7a9bfc3Ss.makeev_local ::SwitchToThread (); 38f7a9bfc3Ss.makeev_local } 39f7a9bfc3Ss.makeev_local 403d930776Ss.makeev_local 413d930776Ss.makeev_local class ThreadId 423d930776Ss.makeev_local { 4347ecee31Ss.makeev_local protected: 443d930776Ss.makeev_local MW_DWORD id; 453d930776Ss.makeev_local Atomic32<uint32> isInitialized; 463d930776Ss.makeev_local Assign(const ThreadId & other)479c716f68Ss.makeev_local void Assign(const ThreadId& other) 489c716f68Ss.makeev_local { 499c716f68Ss.makeev_local id = other.id; 509c716f68Ss.makeev_local isInitialized.Store(other.isInitialized.Load()); 519c716f68Ss.makeev_local } 523d930776Ss.makeev_local 539c716f68Ss.makeev_local public: 543d930776Ss.makeev_local ThreadId()553d930776Ss.makeev_local ThreadId() 563d930776Ss.makeev_local { 573d930776Ss.makeev_local isInitialized.Store(0); 583d930776Ss.makeev_local } 593d930776Ss.makeev_local ThreadId(const ThreadId & other)60b2d53818Ss.makeev_local mt_forceinline ThreadId(const ThreadId& other) 613d930776Ss.makeev_local { 629c716f68Ss.makeev_local Assign(other); 633d930776Ss.makeev_local } 643d930776Ss.makeev_local 65b2d53818Ss.makeev_local mt_forceinline ThreadId& operator=(const ThreadId& other) 669c716f68Ss.makeev_local { 679c716f68Ss.makeev_local Assign(other); 689c716f68Ss.makeev_local return *this; 699c716f68Ss.makeev_local } 709c716f68Ss.makeev_local Self()71b2d53818Ss.makeev_local mt_forceinline static ThreadId Self() 729c716f68Ss.makeev_local { 739c716f68Ss.makeev_local ThreadId selfThread; 749c716f68Ss.makeev_local selfThread.id = ::GetCurrentThreadId(); 759c716f68Ss.makeev_local selfThread.isInitialized.Store(1); 769c716f68Ss.makeev_local return selfThread; 779c716f68Ss.makeev_local } 789c716f68Ss.makeev_local IsValid()79b2d53818Ss.makeev_local mt_forceinline bool IsValid() const 809c716f68Ss.makeev_local { 819c716f68Ss.makeev_local return (isInitialized.Load() != 0); 829c716f68Ss.makeev_local } 839c716f68Ss.makeev_local IsEqual(const ThreadId & other)8447ecee31Ss.makeev_local mt_forceinline bool IsEqual(const ThreadId& other) const 859c716f68Ss.makeev_local { 869c716f68Ss.makeev_local if (isInitialized.Load() != other.isInitialized.Load()) 879c716f68Ss.makeev_local { 889c716f68Ss.makeev_local return false; 899c716f68Ss.makeev_local } 909c716f68Ss.makeev_local if (id != other.id) 919c716f68Ss.makeev_local { 929c716f68Ss.makeev_local return false; 939c716f68Ss.makeev_local } 949c716f68Ss.makeev_local return true; 959c716f68Ss.makeev_local } 969c716f68Ss.makeev_local AsUInt64()97b2d53818Ss.makeev_local mt_forceinline uint64 AsUInt64() const 983d930776Ss.makeev_local { 993d930776Ss.makeev_local if (isInitialized.Load() == 0) 1003d930776Ss.makeev_local { 1019c716f68Ss.makeev_local return (uint64)-1; 1023d930776Ss.makeev_local } 1033d930776Ss.makeev_local 1049c716f68Ss.makeev_local return (uint64)id; 1053d930776Ss.makeev_local } 1069c716f68Ss.makeev_local 1073d930776Ss.makeev_local }; 1083d930776Ss.makeev_local 10947d53e4dSSergey Makeev 11047d53e4dSSergey Makeev class Thread : public ThreadBase 11147d53e4dSSergey Makeev { 112d0d10efeSs.makeev MW_HANDLE thread; 11347d53e4dSSergey Makeev ThreadFuncInternal(void * pThread)114d0d10efeSs.makeev static MW_DWORD __stdcall ThreadFuncInternal(void *pThread) 11547d53e4dSSergey Makeev { 11647d53e4dSSergey Makeev Thread* self = (Thread*)pThread; 11747d53e4dSSergey Makeev self->func(self->funcData); 1183b52e8bcSSergey Makeev return 0; 11947d53e4dSSergey Makeev } 120d7cf17b1Ss.makeev_local 121d7cf17b1Ss.makeev_local 122d7cf17b1Ss.makeev_local GetPriority(ThreadPriority::Type priority)123d7cf17b1Ss.makeev_local static int GetPriority(ThreadPriority::Type priority) 124d7cf17b1Ss.makeev_local { 125d7cf17b1Ss.makeev_local switch(priority) 126d7cf17b1Ss.makeev_local { 127d7cf17b1Ss.makeev_local case ThreadPriority::DEFAULT: 128d7cf17b1Ss.makeev_local return MW_THREAD_PRIORITY_HIGHEST; 129d7cf17b1Ss.makeev_local case ThreadPriority::HIGH: 130d7cf17b1Ss.makeev_local return MW_THREAD_PRIORITY_NORMAL; 131d7cf17b1Ss.makeev_local case ThreadPriority::LOW: 132d7cf17b1Ss.makeev_local return MW_THREAD_PRIORITY_LOWEST; 133d7cf17b1Ss.makeev_local default: 134d7cf17b1Ss.makeev_local MT_REPORT_ASSERT("Invalid thread priority"); 135d7cf17b1Ss.makeev_local } 136d7cf17b1Ss.makeev_local 137d7cf17b1Ss.makeev_local return MW_THREAD_PRIORITY_NORMAL; 138d7cf17b1Ss.makeev_local } 139d7cf17b1Ss.makeev_local 140d7cf17b1Ss.makeev_local 14147d53e4dSSergey Makeev public: 14247d53e4dSSergey Makeev Thread()14347d53e4dSSergey Makeev Thread() 14447d53e4dSSergey Makeev : thread(nullptr) 14547d53e4dSSergey Makeev { 14647d53e4dSSergey Makeev } 14747d53e4dSSergey Makeev ~Thread()14847d53e4dSSergey Makeev ~Thread() 14947d53e4dSSergey Makeev { 15034a394c3SSergey Makeev MT_ASSERT(thread == nullptr, "Thread is not stopped!"); 15147d53e4dSSergey Makeev } 15247d53e4dSSergey Makeev 153d7cf17b1Ss.makeev_local void Start(size_t stackSize, TThreadEntryPoint entryPoint, void *userData, uint32 cpuCore = MT_CPUCORE_ANY, ThreadPriority::Type priority = ThreadPriority::DEFAULT) 15447d53e4dSSergey Makeev { 15534a394c3SSergey Makeev MT_ASSERT(thread == nullptr, "Thread already started"); 15647d53e4dSSergey Makeev 15747d53e4dSSergey Makeev func = entryPoint; 15847d53e4dSSergey Makeev funcData = userData; 159d7cf17b1Ss.makeev_local thread = ::CreateThread( nullptr, stackSize, ThreadFuncInternal, this, MW_CREATE_SUSPENDED, nullptr ); 16034a394c3SSergey Makeev MT_ASSERT(thread != nullptr, "Can't create thread"); 161d7cf17b1Ss.makeev_local 162d7cf17b1Ss.makeev_local if (cpuCore == MT_CPUCORE_ANY) 163d7cf17b1Ss.makeev_local { 164d7cf17b1Ss.makeev_local cpuCore = MW_MAXIMUM_PROCESSORS; 165d7cf17b1Ss.makeev_local } 166*ca5ef20aSDmitry Tsarevich MT_VERIFY((cpuCore < (uint32)GetNumberOfHardwareThreads()) || cpuCore == MW_MAXIMUM_PROCESSORS, "Invalid cpu core specified", cpuCore=MW_MAXIMUM_PROCESSORS); 167d7cf17b1Ss.makeev_local MW_DWORD res = ::SetThreadIdealProcessor(thread, cpuCore); 168d7cf17b1Ss.makeev_local MT_USED_IN_ASSERT(res); 169d7cf17b1Ss.makeev_local MT_ASSERT(res != (MW_DWORD)-1, "SetThreadIdealProcessor failed!"); 170d7cf17b1Ss.makeev_local 171d7cf17b1Ss.makeev_local int sched_priority = GetPriority(priority); 172d7cf17b1Ss.makeev_local 173d7cf17b1Ss.makeev_local MW_BOOL result = ::SetThreadPriority(thread, sched_priority); 174d7cf17b1Ss.makeev_local MT_USED_IN_ASSERT(result); 175d7cf17b1Ss.makeev_local MT_ASSERT(result != 0, "SetThreadPriority failed!"); 176d7cf17b1Ss.makeev_local 177d7cf17b1Ss.makeev_local res = ::ResumeThread(thread); 178d7cf17b1Ss.makeev_local MT_USED_IN_ASSERT(res); 179d7cf17b1Ss.makeev_local MT_ASSERT(res != (MW_DWORD)-1, "ResumeThread failed!"); 18047d53e4dSSergey Makeev } 18147d53e4dSSergey Makeev Join()182c7362320Ss.makeev_local void Join() 18347d53e4dSSergey Makeev { 18447d53e4dSSergey Makeev if (thread == nullptr) 18547d53e4dSSergey Makeev { 18647d53e4dSSergey Makeev return; 18747d53e4dSSergey Makeev } 18847d53e4dSSergey Makeev 189d0d10efeSs.makeev ::WaitForSingleObject(thread, MW_INFINITE); 190d0d10efeSs.makeev MW_BOOL res = CloseHandle(thread); 1912e846c40SSergey Makeev MT_USED_IN_ASSERT(res); 19234a394c3SSergey Makeev MT_ASSERT(res != 0, "Can't close thread handle"); 19347d53e4dSSergey Makeev thread = nullptr; 19447d53e4dSSergey Makeev } 19547d53e4dSSergey Makeev 196c88507a8Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD SetThreadName(const char * threadName)197d7cf17b1Ss.makeev_local static void SetThreadName(const char* threadName) 198d7cf17b1Ss.makeev_local { 199c88507a8Ss.makeev_local const int MW_EXCEPTION_EXECUTE_HANDLER = 1; 200c88507a8Ss.makeev_local const MW_DWORD MW_MSVC_EXCEPTION = 0x406D1388; 201c88507a8Ss.makeev_local 202c88507a8Ss.makeev_local #pragma pack(push,8) 203c88507a8Ss.makeev_local typedef struct tagTHREADNAME_INFO 204c88507a8Ss.makeev_local { 205c88507a8Ss.makeev_local MW_DWORD dwType; // Must be 0x1000. 206c88507a8Ss.makeev_local const char* szName; // Pointer to name (in user addr space). 207c88507a8Ss.makeev_local MW_DWORD dwThreadID; // Thread ID (-1=caller thread). 208c88507a8Ss.makeev_local MW_DWORD dwFlags; // Reserved for future use, must be zero. 209c88507a8Ss.makeev_local } THREADNAME_INFO; 210c88507a8Ss.makeev_local #pragma pack(pop) 211c88507a8Ss.makeev_local 212c88507a8Ss.makeev_local THREADNAME_INFO info; 213c88507a8Ss.makeev_local info.dwType = 0x1000; 214c88507a8Ss.makeev_local info.szName = threadName; 2153d930776Ss.makeev_local info.dwThreadID = ::GetCurrentThreadId(); 216c88507a8Ss.makeev_local info.dwFlags = 0; 217c88507a8Ss.makeev_local 218c88507a8Ss.makeev_local __try 219c88507a8Ss.makeev_local { 220c88507a8Ss.makeev_local RaiseException(MW_MSVC_EXCEPTION, 0, sizeof(info) / sizeof(void*), (MW_ULONG_PTR*)&info); 221c88507a8Ss.makeev_local } 222c88507a8Ss.makeev_local __except (MW_EXCEPTION_EXECUTE_HANDLER) 223c88507a8Ss.makeev_local { 224c88507a8Ss.makeev_local } 225c88507a8Ss.makeev_local } 226d7cf17b1Ss.makeev_local #endif 227c88507a8Ss.makeev_local 228d7cf17b1Ss.makeev_local static void SetThreadSchedulingPolicy(uint32 cpuCore, ThreadPriority::Type priority = ThreadPriority::DEFAULT) 229d7cf17b1Ss.makeev_local { 230d7cf17b1Ss.makeev_local if (cpuCore == MT_CPUCORE_ANY) 231d7cf17b1Ss.makeev_local { 232d7cf17b1Ss.makeev_local cpuCore = MW_MAXIMUM_PROCESSORS; 233d7cf17b1Ss.makeev_local } 234*ca5ef20aSDmitry Tsarevich MT_VERIFY((cpuCore < (uint32)GetNumberOfHardwareThreads()) || cpuCore == MW_MAXIMUM_PROCESSORS, "Invalid cpu core specified", cpuCore=MW_MAXIMUM_PROCESSORS); 235d7cf17b1Ss.makeev_local MW_DWORD res = ::SetThreadIdealProcessor( ::GetCurrentThread(), cpuCore); 236d7cf17b1Ss.makeev_local MT_USED_IN_ASSERT(res); 237d7cf17b1Ss.makeev_local MT_ASSERT(res != (MW_DWORD)-1, "SetThreadIdealProcessor failed!"); 238d7cf17b1Ss.makeev_local 239d7cf17b1Ss.makeev_local int sched_priority = GetPriority(priority); 240d7cf17b1Ss.makeev_local 241d7cf17b1Ss.makeev_local MW_BOOL result = ::SetThreadPriority( ::GetCurrentThread(), sched_priority ); 242d7cf17b1Ss.makeev_local MT_USED_IN_ASSERT(result); 243d7cf17b1Ss.makeev_local MT_ASSERT(result != 0, "SetThreadPriority failed!"); 244d7cf17b1Ss.makeev_local } 245c88507a8Ss.makeev_local GetNumberOfHardwareThreads()24647d53e4dSSergey Makeev static int GetNumberOfHardwareThreads() 24747d53e4dSSergey Makeev { 248d0d10efeSs.makeev MW_SYSTEM_INFO sysinfo; 24947d53e4dSSergey Makeev ::GetSystemInfo( &sysinfo ); 25047d53e4dSSergey Makeev return sysinfo.dwNumberOfProcessors; 25147d53e4dSSergey Makeev } 25247d53e4dSSergey Makeev Sleep(uint32 milliseconds)25347d53e4dSSergey Makeev static void Sleep(uint32 milliseconds) 25447d53e4dSSergey Makeev { 25547d53e4dSSergey Makeev ::Sleep(milliseconds); 25647d53e4dSSergey Makeev } 25747d53e4dSSergey Makeev }; 25847d53e4dSSergey Makeev 25947d53e4dSSergey Makeev 26047d53e4dSSergey Makeev } 26147d53e4dSSergey Makeev 26247d53e4dSSergey Makeev 26381ec7369SSergey Makeev #endif 264