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 <UnitTest++.h>
25 #include <MTScheduler.h>
26 
27 #include "../Profiler/Profiler.h"
28 
29 
30 #ifdef MT_THREAD_SANITIZER
31 	#define MT_DEFAULT_WAIT_TIME (500000)
32 	#define MT_SUBTASK_QUEUE_DEEP (3)
33 	#define MT_ITERATIONS_COUNT (10)
34 #else
35 	#define MT_DEFAULT_WAIT_TIME (5000)
36 	#define MT_SUBTASK_QUEUE_DEEP (12)
37 	#define MT_ITERATIONS_COUNT (100000)
38 #endif
39 
40 SUITE(SubtasksTests)
41 {
42 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
43 template<size_t N>
44 struct DeepSubtaskQueue
45 {
46 	MT_DECLARE_TASK(DeepSubtaskQueue<N>, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
47 
48 	int result;
49 
50 	DeepSubtaskQueue() : result(0) {}
51 
52 	void Do(MT::FiberContext& context)
53 	{
54 		DeepSubtaskQueue<N - 1> taskNm1;
55 		DeepSubtaskQueue<N - 2> taskNm2;
56 
57 		context.RunSubtasksAndYield(MT::TaskGroup::Default(), &taskNm1, 1);
58 		context.RunSubtasksAndYield(MT::TaskGroup::Default(), &taskNm2, 1);
59 
60 		result = taskNm2.result + taskNm1.result;
61 	}
62 };
63 
64 template<>
65 struct DeepSubtaskQueue<0>
66 {
67 	MT_DECLARE_TASK(DeepSubtaskQueue<0>, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
68 
69 	int result;
70 	void Do(MT::FiberContext&)
71 	{
72 		result = 0;
73 	}
74 };
75 
76 
77 template<>
78 struct DeepSubtaskQueue<1>
79 {
80 	MT_DECLARE_TASK(DeepSubtaskQueue<1>, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
81 
82 	int result;
83 	void Do(MT::FiberContext&)
84 	{
85 		result = 1;
86 	}
87 };
88 
89 
90 //
91 TEST(DeepSubtaskQueue)
92 {
93 	MT::TaskScheduler scheduler;
94 
95 	DeepSubtaskQueue<MT_SUBTASK_QUEUE_DEEP> task;
96 	scheduler.RunAsync(MT::TaskGroup::Default(), &task, 1);
97 
98 	CHECK(scheduler.WaitAll(MT_DEFAULT_WAIT_TIME));
99 	CHECK_EQUAL(task.result, 144);
100 }
101 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
102 
103 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
104 static MT::TaskGroup sourceGroup;
105 static MT::TaskGroup resultGroup;
106 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
107 struct GroupSubtask
108 {
109 	MT_DECLARE_TASK(GroupSubtask, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
110 
111 	void Do(MT::FiberContext& context)
112 	{
113 		resultGroup = context.currentGroup;
114 	}
115 };
116 
117 struct GroupTask
118 {
119 	MT_DECLARE_TASK(GroupTask, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
120 
121 	void Do(MT::FiberContext& context)
122 	{
123 		GroupSubtask task;
124 		context.RunSubtasksAndYield(sourceGroup, &task, 1);
125 	}
126 };
127 
128 struct TaskWithManySubtasks
129 {
130 	MT_DECLARE_TASK(TaskWithManySubtasks, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
131 
132 	void Do(MT::FiberContext& context)
133 	{
134 		GroupTask task;
135 		for (int i = 0; i < 2; ++i)
136 		{
137 			context.RunSubtasksAndYield(MT::TaskGroup::Default(), &task, 1);
138 			MT::Thread::SpinSleepMilliSeconds(1);
139 		}
140 	}
141 
142 };
143 
144 //
145 TEST(SubtaskGroup)
146 {
147 	MT::TaskScheduler scheduler;
148 
149 	sourceGroup = scheduler.CreateGroup();
150 
151 	GroupTask task;
152 	scheduler.RunAsync(sourceGroup, &task, 1);
153 
154 	CHECK(scheduler.WaitAll(MT_DEFAULT_WAIT_TIME));
155 
156 	CHECK_EQUAL(sourceGroup.GetValidIndex(), resultGroup.GetValidIndex());
157 }
158 
159 // Checks task with multiple subtasks
160 TEST(OneTaskManySubtasks)
161 {
162 	MT::TaskScheduler scheduler;
163 
164 	sourceGroup = scheduler.CreateGroup();
165 
166 	TaskWithManySubtasks task;
167 	scheduler.RunAsync(MT::TaskGroup::Default(), &task, 1);
168 	CHECK(scheduler.WaitAll(MT_DEFAULT_WAIT_TIME));
169 }
170 
171 // Checks many simple task with subtasks
172 TEST(ManyTasksOneSubtask)
173 {
174 /*
175 	MT::Thread::SetThreadSchedulingPolicy(0, MT::ThreadPriority::DEFAULT);
176 
177 	MT::WorkerThreadParams singleCoreParams;
178 	singleCoreParams.core = 1;
179 	singleCoreParams.priority = MT::ThreadPriority::DEFAULT;
180 
181 #ifdef MT_INSTRUMENTED_BUILD
182 	MT::TaskScheduler scheduler(1, &singleCoreParams, GetProfiler());
183 #else
184 	MT::TaskScheduler scheduler(1, &singleCoreParams);
185 #endif
186 */
187 
188 	MT::TaskScheduler scheduler;
189 
190 	bool waitAllOK = true;
191 
192 	sourceGroup = scheduler.CreateGroup();
193 
194 	for (int i = 0; i < MT_ITERATIONS_COUNT; ++i)
195 	{
196 #ifdef MT_INSTRUMENTED_BUILD
197 		PushPerfEvent("Iteration");
198 #endif
199 
200 		GroupTask group;
201 		scheduler.RunAsync(sourceGroup, &group, 1);
202 		//if (!scheduler.WaitAll(MT_DEFAULT_WAIT_TIME))
203 		if (!scheduler.WaitGroup(sourceGroup, MT_DEFAULT_WAIT_TIME))
204 		{
205 			printf("Timeout: Failed iteration %d\n", i);
206 			waitAllOK = false;
207 			break;
208 		}
209 
210 #ifdef MT_INSTRUMENTED_BUILD
211 		PopPerfEvent("Iteration");
212 #endif
213 	}
214 
215 
216 	CHECK(waitAllOK);
217 }
218 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
219 
220 
221 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
222 
223 struct TaskSubtaskCombo_Sum1
224 {
225 	MT_DECLARE_TASK(TaskSubtaskCombo_Sum1, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
226 
227 	MT::Atomic32<int32>* data;
228 
229 	void Do(MT::FiberContext&)
230 	{
231 		data->IncFetch();
232 	}
233 };
234 
235 struct TaskSubtaskCombo_Sum4
236 {
237 	MT_DECLARE_TASK(TaskSubtaskCombo_Sum4, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
238 
239 	MT::Atomic32<int32>* data;
240 
241 	TaskSubtaskCombo_Sum1 tasks[2];
242 
243 	void Do(MT::FiberContext& context)
244 	{
245 		tasks[0].data = data;
246 		tasks[1].data = data;
247 
248 		context.RunAsync(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
249 		context.RunSubtasksAndYield(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
250 	}
251 };
252 
253 struct TaskSubtaskCombo_Sum16
254 {
255 	MT_DECLARE_TASK(TaskSubtaskCombo_Sum16, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
256 
257 	MT::Atomic32<int32>* data;
258 
259 	TaskSubtaskCombo_Sum4 tasks[2];
260 
261 	void Do(MT::FiberContext& context)
262 	{
263 		tasks[0].data = data;
264 		tasks[1].data = data;
265 
266 		context.RunAsync(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
267 		context.RunSubtasksAndYield(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
268 	}
269 };
270 
271 MT::Atomic32<int32> sum;
272 
273 //
274 TEST(TaskSubtaskCombo)
275 {
276 	sum.Store(0);
277 
278 	MT::TaskScheduler scheduler;
279 
280 	TaskSubtaskCombo_Sum16 task[16];
281 	for (int i = 0; i < 16; ++i)
282 	{
283 		task[i].data = &sum;
284 		scheduler.RunAsync(MT::TaskGroup::Default(), &task[i], 1);
285 	}
286 
287 	CHECK(scheduler.WaitAll(MT_DEFAULT_WAIT_TIME));
288 
289 	CHECK_EQUAL(sum.Load(), 256);
290 }
291 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
292 }
293