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 #pragma once 24 25 26 27 namespace MT 28 { 29 30 namespace TaskID 31 { 32 //unused_id is any odd number, valid_id should be always only even numbers 33 static const int UNUSED = 1; 34 } 35 36 37 //forward declaration 38 class TaskHandle; 39 40 41 /// \class TaskPoolBase 42 /// \brief 43 ////////////////////////////////////////////////////////////////////////// 44 struct PoolElementHeader 45 { 46 AtomicInt id; 47 48 protected: 49 50 virtual void Destroy() 51 { 52 id.Set(TaskID::UNUSED); 53 } 54 55 public: 56 57 PoolElementHeader(int _id) 58 : id(_id) 59 { 60 } 61 62 static bool DestoryByHandle(const MT::TaskHandle & handle); 63 }; 64 65 66 /// \class TaskPoolElement 67 /// \brief 68 ////////////////////////////////////////////////////////////////////////// 69 template<typename T> 70 class PoolElement : public PoolElementHeader 71 { 72 protected: 73 74 virtual void Destroy() 75 { 76 PoolElementHeader::Destroy(); 77 //call dtor 78 task.~T(); 79 } 80 81 public: 82 83 T task; 84 85 PoolElement(int _id, T && _task) 86 : PoolElementHeader(_id) 87 , task( std::move(_task) ) 88 { 89 } 90 91 }; 92 93 94 /// \class TaskHandle 95 /// \brief 96 ////////////////////////////////////////////////////////////////////////// 97 class TaskHandle 98 { 99 100 int check_id; 101 102 protected: 103 104 friend struct PoolElementHeader; 105 PoolElementHeader * task; 106 107 public: 108 109 //default ctor 110 TaskHandle() 111 : check_id(TaskID::UNUSED) 112 , task(nullptr) 113 { 114 115 } 116 117 //ctor 118 TaskHandle(int _id, PoolElementHeader* _task) 119 : check_id(_id) 120 , task(_task) 121 { 122 } 123 124 //copy ctor 125 TaskHandle(const TaskHandle & other) 126 : check_id(other.check_id) 127 , task(other.task) 128 { 129 } 130 131 //move ctor 132 TaskHandle(TaskHandle && other) 133 : check_id(other.check_id) 134 , task(other.task) 135 { 136 other.check_id = TaskID::UNUSED; 137 other.task = nullptr; 138 } 139 140 ~TaskHandle() 141 { 142 } 143 144 bool IsValid() const 145 { 146 if (task == nullptr) 147 { 148 return false; 149 } 150 151 if (check_id != task->id.Get()) 152 { 153 return false; 154 } 155 156 return true; 157 } 158 159 }; 160 161 162 163 164 ////////////////////////////////////////////////////////////////////////// 165 inline bool PoolElementHeader::DestoryByHandle(const MT::TaskHandle & handle) 166 { 167 if (!handle.IsValid()) 168 { 169 return false; 170 } 171 172 handle.task->Destroy(); 173 return true; 174 } 175 176 177 178 179 180 /// \class TaskPool 181 /// \brief 182 ////////////////////////////////////////////////////////////////////////// 183 template<typename T, size_t N> 184 class TaskPool 185 { 186 typedef PoolElement<T> PoolItem; 187 188 // 189 static const size_t MASK = (N - 1); 190 191 void* data; 192 AtomicInt idGenerator; 193 AtomicInt index; 194 195 inline PoolItem* Buffer() 196 { 197 return (PoolItem*)(data); 198 } 199 200 inline void MoveCtor(PoolItem* element, int id, T && val) 201 { 202 new(element) PoolItem(id, std::move(val)); 203 } 204 205 private: 206 207 TaskPool(const TaskPool &) {} 208 void operator=(const TaskPool &) {} 209 210 public: 211 212 TaskPool() 213 : idGenerator(0) 214 , index(0) 215 { 216 static_assert( MT::StaticIsPow2<N>::result, "Task pool capacity must be power of 2"); 217 218 size_t bytesCount = sizeof(PoolItem) * N; 219 data = Memory::Alloc(bytesCount); 220 221 for(size_t idx = 0; idx < N; idx++) 222 { 223 PoolItem* pElement = Buffer() + idx; 224 pElement->id.Set(TaskID::UNUSED); 225 } 226 } 227 228 ~TaskPool() 229 { 230 if (data != nullptr) 231 { 232 233 for(size_t idx = 0; idx < N; idx++) 234 { 235 PoolItem* pElement = Buffer() + idx; 236 237 int preValue = pElement->id.Set(TaskID::UNUSED); 238 if (preValue != TaskID::UNUSED) 239 { 240 pElement->task.~T(); 241 } 242 } 243 244 Memory::Free(data); 245 data = nullptr; 246 } 247 } 248 249 250 TaskHandle Alloc(T && task) 251 { 252 int idx = index.Inc() - 1; 253 254 int clampedIdx = (idx & MASK); 255 256 PoolItem* pElement = Buffer() + clampedIdx; 257 258 bool isUnused = ((pElement->id.Get() & 1 ) != 0); 259 if (isUnused == false) 260 { 261 //Can't allocate more, next element in circular buffer is already used 262 return TaskHandle(); 263 } 264 265 //generate next even number for id 266 int id = idGenerator.Add(2); 267 MoveCtor( pElement, id, std::move(task) ); 268 return TaskHandle(id, pElement); 269 } 270 271 }; 272 273 } 274