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 
23 #include "Tests.h"
24 #include "../Profiler/Profiler.h"
25 #include <UnitTest++.h>
26 #include <MTScheduler.h>
27 
SUITE(PlatformTests)28 SUITE(PlatformTests)
29 {
30 	static const intptr_t DATA_VALUE = 13;
31 
32 	intptr_t g_Variable = 0;
33 
34 	void MyThreadFunc(void* userData)
35 	{
36 		intptr_t data = (intptr_t)userData;
37 
38 		CHECK(data == DATA_VALUE);
39 
40 		g_Variable = data;
41 	}
42 
43 	TEST(ThreadTest)
44 	{
45 		intptr_t data = DATA_VALUE;
46 
47 		MT::Thread thread;
48 		thread.Start(32768, MyThreadFunc, (void*)data);
49 		thread.Join();
50 
51 		CHECK(g_Variable == DATA_VALUE);
52 	}
53 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
54 
55 	MT::Event *pEvent1 = nullptr;
56 	MT::Event *pEvent2 = nullptr;
57 
58 	void MyThreadFunc2(void*)
59 	{
60 		MT::SpinSleepMilliSeconds(300);
61 		pEvent1->Signal();
62 	}
63 
64 	TEST(EventTest)
65 	{
66 		MT::Event event1;
67 		event1.Create(MT::EventReset::MANUAL, false);
68 		pEvent1 = &event1;
69 
70 		MT::Event event2;
71 		event2.Create(MT::EventReset::AUTOMATIC, false);
72 		pEvent2 = &event2;
73 
74 		MT::Thread thread;
75 		thread.Start(32768, MyThreadFunc2, nullptr);
76 
77 		bool res0 = event1.Wait(100);
78 		bool res1 = event1.Wait(1000);
79 		bool res2 = event1.Wait(1000);
80 		event1.Reset();
81 		bool res3 = event1.Wait(100);
82 
83 		CHECK(!res0);
84 		CHECK(res1);
85 		CHECK(res2);
86 		CHECK(!res3);
87 
88 		bool res4 = event2.Wait(100);
89 		CHECK(!res4);
90 		bool res5 = event2.Wait(100);
91 		CHECK(!res5);
92 		bool res6 = event2.Wait(100);
93 		CHECK(!res6);
94 
95 		thread.Join();
96 	}
97 
98 
99 
100 	MT::AtomicPtrBase<MT::Event> pStressEvent = { nullptr };
101 	MT::Atomic32Base<uint32> needExitSignal = { 0 };
102 	MT::Atomic32Base<uint32> needExitWait = { 0 };
103 	MT::Atomic32Base<uint32> needStartWork = { 0 };
104 
105 	void EventStressTestSignalThreadFunc(void*)
106 	{ BROFILER_THREAD("SignalThread");
107 
108 		while (needStartWork.Load() == 0)
109 		{ BROFILER_CATEGORY("Signal prepare", 0xFFFF00FF);
110 			MT::SpinSleepMicroSeconds(300);
111 		}
112 
113 		while (needExitSignal.Load() == 0)
114 		{ BROFILER_CATEGORY("Signal Loop", 0xFF556B2F);
115 			MT::SpinSleepMicroSeconds(300);
116 			MT::Event * pEvent = pStressEvent.Load();
117 			pEvent->Signal();
118 		}
119 	}
120 
121 	void EventStressTestWaitThreadFunc(void*)
122 	{ BROFILER_THREAD("WaitThread");
123 
124 		while (needStartWork.Load() == 0)
125 		{ BROFILER_CATEGORY("Wait prepare", 0xFFFF00FF);
126 			MT::SpinSleepMicroSeconds(300);
127 		}
128 
129 		while (needExitWait.Load() == 0)
130 		{ BROFILER_CATEGORY("Wait Loop", 0xFFF0F8FF);
131 			MT::Event* pEvent = pStressEvent.Load();
132 			bool res = pEvent->Wait(1000);
133 			MT::SpinSleepMicroSeconds(300);
134 			CHECK(res == true);
135 		}
136 	}
137 
138 
139 
140 
141 
142 	TEST(EventStressTest)
143 	{
144 #if defined(MT_ENABLE_LEGACY_WINDOWSXP_SUPPORT) && defined(MT_PLATFORM_WINDOWS)
145 		printf("Kernel mode events\n");
146 #else
147 		printf("User mode events\n");
148 #endif
149 
150 
151 		MT::Event stressEvent;
152 		MT::Thread signalThreads[3];
153 		MT::Thread waitThreads[3];
154 		needStartWork.Store(0);
155 
156 		{
157 			BROFILER_NEXT_FRAME();
158 			stressEvent.Create(MT::EventReset::AUTOMATIC, false);
159 			pStressEvent.Store( &stressEvent );
160 
161 			needExitSignal.Store(0);
162 			needExitWait.Store(0);
163 
164 			for(uint32 i = 0; i < MT_ARRAY_SIZE(signalThreads); i++)
165 			{
166 				signalThreads[i].Start(32768, EventStressTestSignalThreadFunc, nullptr);
167 			}
168 
169 			for(uint32 i = 0; i < MT_ARRAY_SIZE(waitThreads); i++)
170 			{
171 				waitThreads[i].Start(32768, EventStressTestWaitThreadFunc, nullptr);
172 			}
173 
174 			printf("Signal threads num = %d\n", (uint32)MT_ARRAY_SIZE(signalThreads));
175 			printf("Wait threads num = %d\n", (uint32)MT_ARRAY_SIZE(waitThreads));
176 		}
177 
178 
179 #if defined(MT_INSTRUMENTED_BUILD) && defined(MT_ENABLE_BROFILER_SUPPORT)
180 		BROFILER_FRAME("EventStressTest");
181 		{
182 			printf("Waiting for 'Brofiler' connection.\n");
183 			for(;;)
184 			{
185 				BROFILER_NEXT_FRAME();
186 				MT::Thread::Sleep(150);
187 				if (Brofiler::IsActive())
188 				{
189 					break;
190 				}
191 			}
192 		}
193 #endif
194 		needStartWork.Store(1);
195 
196 		const int iterationsCount = 5000;
197 
198 		int64 startTimeSignal = MT::GetTimeMicroSeconds();
199 		{ BROFILER_NEXT_FRAME();
200 		  BROFILER_CATEGORY("Signal Loop", 0xFF556B2F);
201 			for(int i = 0; i < iterationsCount; i++)
202 			{
203 				stressEvent.Signal();
204 			}
205 		}
206 		int64 endTimeSignal = MT::GetTimeMicroSeconds();
207 
208 
209 		int64 startTimeWait = MT::GetTimeMicroSeconds();
210 		{ BROFILER_NEXT_FRAME();
211 		  BROFILER_CATEGORY("Wait Loop", 0xFFF0F8FF);
212 			for(int i = 0; i < iterationsCount; i++)
213 			{
214 				bool res = stressEvent.Wait(1000);
215 				CHECK(res == true);
216 			}
217 		}
218 		int64 endTimeWait = MT::GetTimeMicroSeconds();
219 
220 		double microSecondsPerWait = (double)((endTimeWait - startTimeWait) / (double)iterationsCount);
221 		double microSecondsPerSignal = (double)((endTimeSignal - startTimeSignal) / (double)iterationsCount);
222 
223 		printf("microseconds per signal = %3.2f, iterations = %d\n", microSecondsPerSignal, iterationsCount);
224 		printf("microseconds per wait = %3.2f, iterations = %d\n", microSecondsPerWait, iterationsCount);
225 
226 #if defined(MT_INSTRUMENTED_BUILD) && defined(MT_ENABLE_BROFILER_SUPPORT)
227 		{
228 			printf("Waiting for the 'Brofiler' to be disconnected\n");
229 			for(;;)
230 			{
231 				BROFILER_NEXT_FRAME();
232 				MT::Thread::Sleep(150);
233 				if (!Brofiler::IsActive())
234 				{
235 					break;
236 				}
237 			}
238 		}
239 
240 		printf("Done\n");
241 #endif
242 
243 		needExitWait.Store(1);
244 		for(uint32 i = 0; i < MT_ARRAY_SIZE(waitThreads); i++)
245 		{
246 			waitThreads[i].Join();
247 		}
248 
249 		MT::Thread::Sleep(100);
250 
251 		needExitSignal.Store(1);
252 		for(uint32 i = 0; i < MT_ARRAY_SIZE(signalThreads); i++)
253 		{
254 			signalThreads[i].Join();
255 		}
256 
257 		bool res = stressEvent.Wait(300);
258 		CHECK(res == true);
259 
260 		res = stressEvent.Wait(300);
261 		CHECK(res == false);
262 
263 	}
264 
265 
266 	TEST(SleepTest)
267 	{
268 
269 		MT::Timer timer;
270 
271 		MT::SpinSleepMilliSeconds(100);
272 
273 		CHECK( timer.GetPastMilliSeconds() >= 100 );
274 	}
275 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
276 }
277