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