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::TaskScheduler scheduler;
173 
174 	bool waitAllOK = true;
175 
176 	sourceGroup = scheduler.CreateGroup();
177 
178 	for (int i = 0; i < MT_ITERATIONS_COUNT; ++i)
179 	{
180 		GroupTask group;
181 		scheduler.RunAsync(sourceGroup, &group, 1);
182 		//if (!scheduler.WaitAll(MT_DEFAULT_WAIT_TIME))
183 		if (!scheduler.WaitGroup(sourceGroup, MT_DEFAULT_WAIT_TIME))
184 		{
185 			printf("Timeout: Failed iteration %d\n", i);
186 			waitAllOK = false;
187 			break;
188 		}
189 	}
190 
191 	CHECK(waitAllOK);
192 }
193 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
194 
195 
196 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
197 
198 struct TaskSubtaskCombo_Sum1
199 {
200 	MT_DECLARE_TASK(TaskSubtaskCombo_Sum1, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
201 
202 	MT::Atomic32<int32>* data;
203 
204 	void Do(MT::FiberContext&)
205 	{
206 		data->IncFetch();
207 	}
208 };
209 
210 struct TaskSubtaskCombo_Sum4
211 {
212 	MT_DECLARE_TASK(TaskSubtaskCombo_Sum4, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
213 
214 	MT::Atomic32<int32>* data;
215 
216 	TaskSubtaskCombo_Sum1 tasks[2];
217 
218 	void Do(MT::FiberContext& context)
219 	{
220 		tasks[0].data = data;
221 		tasks[1].data = data;
222 
223 		context.RunAsync(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
224 		context.RunSubtasksAndYield(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
225 	}
226 };
227 
228 struct TaskSubtaskCombo_Sum16
229 {
230 	MT_DECLARE_TASK(TaskSubtaskCombo_Sum16, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
231 
232 	MT::Atomic32<int32>* data;
233 
234 	TaskSubtaskCombo_Sum4 tasks[2];
235 
236 	void Do(MT::FiberContext& context)
237 	{
238 		tasks[0].data = data;
239 		tasks[1].data = data;
240 
241 		context.RunAsync(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
242 		context.RunSubtasksAndYield(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
243 	}
244 };
245 
246 MT::Atomic32<int32> sum;
247 
248 //
249 TEST(TaskSubtaskCombo)
250 {
251 	sum.Store(0);
252 
253 	MT::TaskScheduler scheduler;
254 
255 	TaskSubtaskCombo_Sum16 task[16];
256 	for (int i = 0; i < 16; ++i)
257 	{
258 		task[i].data = &sum;
259 		scheduler.RunAsync(MT::TaskGroup::Default(), &task[i], 1);
260 	}
261 
262 	CHECK(scheduler.WaitAll(MT_DEFAULT_WAIT_TIME));
263 
264 	CHECK_EQUAL(sum.Load(), 256);
265 }
266 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
267 }
268