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