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