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