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
SUITE(FireAndForget)29 SUITE(FireAndForget)
30 {
31
32 struct SimpleTask;
33
34 typedef MT::TaskPool<SimpleTask, 512> TestPoolType;
35
36 struct SimpleTask
37 {
38 MT_DECLARE_TASK(SimpleTask, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
39
40 MT::Atomic32<int32>* doCounter;
41 MT::Atomic32<int32>* dtorCounter;
42 TestPoolType* taskPool;
43
44 SimpleTask()
45 : doCounter(nullptr)
46 , dtorCounter(nullptr)
47 , taskPool(nullptr)
48 {
49 }
50
51 SimpleTask(MT::Atomic32<int32>* _doCounter, MT::Atomic32<int32>* _dtorCounter, TestPoolType * _taskPool)
52 : doCounter(_doCounter)
53 , dtorCounter(_dtorCounter)
54 , taskPool(_taskPool)
55 {
56 }
57
58 SimpleTask(SimpleTask&& other)
59 : doCounter(other.doCounter)
60 , dtorCounter(other.dtorCounter)
61 , taskPool(other.taskPool)
62 {
63 other.doCounter = nullptr;
64 other.dtorCounter = nullptr;
65 other.taskPool = nullptr;
66 }
67
68 ~SimpleTask()
69 {
70 if (dtorCounter)
71 {
72 dtorCounter->IncFetch();
73 }
74 }
75
76 void Do(MT::FiberContext& context)
77 {
78 if (doCounter)
79 {
80 doCounter->IncFetch();
81 }
82
83 if (taskPool)
84 {
85 MT::TaskHandle handle = taskPool->Alloc(SimpleTask(doCounter, dtorCounter, nullptr));
86
87 context.RunSubtasksAndYield(MT::TaskGroup::Default(), &handle, 1);
88 }
89 }
90 };
91
92
93
94 TEST(SingleThreadPoolTest)
95 {
96 MT::TaskPool<SimpleTask, 4> taskPool;
97
98 MT::TaskHandle taskHandle0 = taskPool.Alloc(SimpleTask());
99 CHECK_EQUAL(true, taskHandle0.IsValid());
100
101 MT::TaskHandle taskHandle1 = taskPool.Alloc(SimpleTask());
102 CHECK_EQUAL(true, taskHandle1.IsValid());
103
104 MT::TaskHandle taskHandle2 = taskPool.Alloc(SimpleTask());
105 CHECK_EQUAL(true, taskHandle2.IsValid());
106
107 MT::TaskHandle taskHandle3 = taskPool.Alloc(SimpleTask());
108 CHECK_EQUAL(true, taskHandle3.IsValid());
109
110 CHECK_EQUAL(true, taskHandle0.IsValid());
111 CHECK_EQUAL(true, taskHandle1.IsValid());
112 CHECK_EQUAL(true, taskHandle2.IsValid());
113 CHECK_EQUAL(true, taskHandle3.IsValid());
114
115
116 // check for allocation fail
117 MT::TaskHandle taskHandle4 = taskPool.TryAlloc(SimpleTask());
118 CHECK_EQUAL(false, taskHandle4.IsValid());
119
120
121 // check state
122 CHECK_EQUAL(true, taskHandle0.IsValid());
123 CHECK_EQUAL(true, taskHandle1.IsValid());
124 CHECK_EQUAL(true, taskHandle2.IsValid());
125 CHECK_EQUAL(true, taskHandle3.IsValid());
126 CHECK_EQUAL(false, taskHandle4.IsValid());
127
128 // destroy pool task by handle
129 CHECK_EQUAL(true, MT::PoolElementHeader::DestoryByHandle(taskHandle0));
130 CHECK_EQUAL(true, MT::PoolElementHeader::DestoryByHandle(taskHandle1));
131 CHECK_EQUAL(true, MT::PoolElementHeader::DestoryByHandle(taskHandle2));
132 CHECK_EQUAL(true, MT::PoolElementHeader::DestoryByHandle(taskHandle3));
133 CHECK_EQUAL(false, MT::PoolElementHeader::DestoryByHandle(taskHandle4));
134
135 // check for double destroy
136 CHECK_EQUAL(false, MT::PoolElementHeader::DestoryByHandle(taskHandle0));
137 CHECK_EQUAL(false, MT::PoolElementHeader::DestoryByHandle(taskHandle3));
138
139 MT::TaskHandle taskHandle5 = taskPool.Alloc(SimpleTask());
140
141 CHECK_EQUAL(false, taskHandle0.IsValid());
142 CHECK_EQUAL(false, taskHandle1.IsValid());
143 CHECK_EQUAL(false, taskHandle2.IsValid());
144 CHECK_EQUAL(false, taskHandle3.IsValid());
145 CHECK_EQUAL(false, taskHandle4.IsValid());
146 CHECK_EQUAL(true, taskHandle5.IsValid());
147 }
148
149
150 struct ThreadTest
151 {
152 MT_DECLARE_TASK(ThreadTest, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
153
154 TestPoolType * taskPool;
155
156 void Do(MT::FiberContext&)
157 {
158 for (int i = 0; i < 20000; i++)
159 {
160 MT::TaskHandle handle = taskPool->TryAlloc(SimpleTask());
161 if (handle.IsValid())
162 {
163 CHECK_EQUAL(true, MT::PoolElementHeader::DestoryByHandle(handle));
164 } else
165 {
166 CHECK_EQUAL(false, MT::PoolElementHeader::DestoryByHandle(handle));
167 }
168 }
169 }
170 };
171
172
173 TEST(MultiThreadPoolTest)
174 {
175 TestPoolType taskPool;
176
177 MT::TaskScheduler scheduler;
178
179 ThreadTest tasks[8];
180 for (size_t i = 0; i < MT_ARRAY_SIZE(tasks); ++i)
181 {
182 tasks[i].taskPool = &taskPool;
183 }
184
185 scheduler.RunAsync(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
186
187 int timeout = 20000;
188 CHECK(scheduler.WaitGroup(MT::TaskGroup::Default(), timeout));
189 }
190
191
192 //
193 TEST(FireAndForgetSimple)
194 {
195 MT::Atomic32<int32> doCounter(0);
196 MT::Atomic32<int32> dtorCounter(0);
197
198 MT::TaskScheduler scheduler;
199 TestPoolType taskPool;
200
201 for(int pass = 0; pass < 4; pass++)
202 {
203 printf("--- step %d ---\n", pass);
204
205 doCounter.Store(0);
206 dtorCounter.Store(0);
207
208 MT::TaskHandle taskHandles[250];
209 for (size_t i = 0; i < MT_ARRAY_SIZE(taskHandles); ++i)
210 {
211 taskHandles[i] = taskPool.Alloc(SimpleTask(&doCounter, &dtorCounter, &taskPool));
212 CHECK_EQUAL(true, taskHandles[i].IsValid());
213 }
214
215 scheduler.RunAsync(MT::TaskGroup::Default(), &taskHandles[0], MT_ARRAY_SIZE(taskHandles));
216
217 int timeout = 20000;
218 CHECK(scheduler.WaitAll(timeout));
219
220 CHECK_EQUAL(MT_ARRAY_SIZE(taskHandles) * 2, (size_t)doCounter.Load());
221 CHECK_EQUAL(MT_ARRAY_SIZE(taskHandles) * 2, (size_t)dtorCounter.Load());
222 }
223
224 }
225
226 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
227 }
228