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 		ThreadId(const ThreadId& other)
60 		{
61 			Assign(other);
62 		}
63 
64 		ThreadId& operator=(const ThreadId& other)
65 		{
66 			Assign(other);
67 			return *this;
68 		}
69 
70 		static ThreadId Self()
71 		{
72 			ThreadId selfThread;
73 			selfThread.id = ::GetCurrentThreadId();
74 			selfThread.isInitialized.Store(1);
75 			return selfThread;
76 		}
77 
78 		bool IsValid() const
79 		{
80 			return (isInitialized.Load() != 0);
81 		}
82 
83 		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 		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