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