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 
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