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