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 #include <MTConfig.h> 26 27 28 namespace MT 29 { 30 31 namespace TaskID 32 { 33 //unused_id is any odd number, valid_id should be always only even numbers 34 static const int UNUSED = 1; 35 } 36 37 38 //forward declaration 39 class TaskHandle; 40 41 42 /// \class PoolElementHeader 43 /// \brief 44 ////////////////////////////////////////////////////////////////////////// 45 struct PoolElementHeader 46 { 47 //Task id (timestamp) 48 Atomic32<int32> id; 49 50 internal::TaskDesc desc; 51 52 public: 53 PoolElementHeaderPoolElementHeader54 PoolElementHeader(int _id) 55 : id(_id) 56 { 57 } 58 59 static bool DestoryByHandle(const MT::TaskHandle & handle); 60 }; 61 62 63 /// \class TaskPoolElement 64 /// \brief 65 ////////////////////////////////////////////////////////////////////////// 66 template<typename T> 67 class PoolElement : public PoolElementHeader 68 { 69 public: 70 71 // Storage for task 72 T task; 73 PoolElement(int _id,T && _task)74 PoolElement(int _id, T && _task) 75 : PoolElementHeader(_id) 76 , task( std::move(_task) ) 77 { 78 79 #if MT_CLANG_COMPILER_FAMILY 80 #pragma clang diagnostic push 81 #pragma clang diagnostic ignored "-Winvalid-offsetof" 82 #endif 83 static_assert( offsetof(PoolElement<T>, task) == sizeof(PoolElementHeader), "Invalid offset for task in PoolElement"); 84 #if MT_CLANG_COMPILER_FAMILY 85 #pragma clang diagnostic pop 86 #endif 87 88 desc.poolDestroyFunc = T::PoolTaskDestroy; 89 desc.taskFunc = T::TaskEntryPoint; 90 desc.stackRequirements = T::GetStackRequirements(); 91 desc.priority = T::GetTaskPriority(); 92 desc.userData = &task; 93 94 #ifdef MT_INSTRUMENTED_BUILD 95 desc.debugID = T::GetDebugID(); 96 desc.debugColor = T::GetDebugColor(); 97 #endif 98 } 99 100 }; 101 102 103 /// \class TaskHandle 104 /// \brief 105 ////////////////////////////////////////////////////////////////////////// 106 class TaskHandle 107 { 108 int32 check_id; 109 110 protected: 111 112 friend struct PoolElementHeader; 113 114 PoolElementHeader* task; 115 116 public: 117 118 //default ctor TaskHandle()119 TaskHandle() 120 : check_id(TaskID::UNUSED) 121 , task(nullptr) 122 { 123 124 } 125 126 //ctor TaskHandle(int _id,PoolElementHeader * _task)127 TaskHandle(int _id, PoolElementHeader* _task) 128 : check_id(_id) 129 , task(_task) 130 { 131 } 132 133 //copy ctor TaskHandle(const TaskHandle & other)134 TaskHandle(const TaskHandle & other) 135 : check_id(other.check_id) 136 , task(other.task) 137 { 138 } 139 140 //move ctor TaskHandle(TaskHandle && other)141 TaskHandle(TaskHandle && other) 142 : check_id(other.check_id) 143 , task(other.task) 144 { 145 other.check_id = TaskID::UNUSED; 146 other.task = nullptr; 147 } 148 ~TaskHandle()149 ~TaskHandle() 150 { 151 } 152 IsValid()153 bool IsValid() const 154 { 155 if (task == nullptr) 156 { 157 return false; 158 } 159 160 if (check_id != task->id.Load()) 161 { 162 return false; 163 } 164 165 return true; 166 } 167 168 169 // assignment operator 170 TaskHandle & operator= (const TaskHandle & other) 171 { 172 check_id = other.check_id; 173 task = other.task; 174 175 return *this; 176 } 177 178 // move assignment operator 179 TaskHandle & operator= (TaskHandle && other) 180 { 181 check_id = other.check_id; 182 task = other.task; 183 184 other.check_id = TaskID::UNUSED; 185 other.task = nullptr; 186 187 return *this; 188 } 189 GetDesc()190 const internal::TaskDesc & GetDesc() const 191 { 192 MT_ASSERT(IsValid(), "Task handle is invalid"); 193 return task->desc; 194 } 195 196 197 }; 198 199 200 201 202 ////////////////////////////////////////////////////////////////////////// DestoryByHandle(const MT::TaskHandle & handle)203 inline bool PoolElementHeader::DestoryByHandle(const MT::TaskHandle & handle) 204 { 205 if (!handle.IsValid()) 206 { 207 return false; 208 } 209 210 if (handle.task->desc.poolDestroyFunc == nullptr) 211 { 212 return false; 213 } 214 215 if (handle.task->desc.userData == nullptr) 216 { 217 return false; 218 } 219 220 //call destroy func 221 handle.task->desc.poolDestroyFunc(handle.task->desc.userData); 222 return true; 223 } 224 225 226 227 228 229 /// \class TaskPool 230 /// \brief 231 ////////////////////////////////////////////////////////////////////////// 232 template<typename T, size_t N> 233 class TaskPool 234 { 235 static const int32 ALIGNMENT = 16; 236 237 typedef PoolElement<T> PoolItem; 238 239 // 240 static const size_t MASK = (N - 1); 241 242 void* data; 243 Atomic32<int32> idGenerator; 244 Atomic32<int32> index; 245 Buffer()246 inline PoolItem* Buffer() 247 { 248 return (PoolItem*)(data); 249 } 250 MoveCtor(PoolItem * element,int id,T && val)251 inline void MoveCtor(PoolItem* element, int id, T && val) 252 { 253 new(element) PoolItem(id, std::move(val)); 254 } 255 256 public: 257 258 MT_NOCOPYABLE(TaskPool); 259 TaskPool()260 TaskPool() 261 : idGenerator(0) 262 , index(0) 263 { 264 static_assert( MT::StaticIsPow2<N>::result, "Task pool capacity must be power of 2"); 265 266 size_t bytesCount = sizeof(PoolItem) * N; 267 data = Memory::Alloc(bytesCount, ALIGNMENT); 268 269 for(size_t idx = 0; idx < N; idx++) 270 { 271 PoolItem* pElement = Buffer() + idx; 272 pElement->id.Store(TaskID::UNUSED); 273 } 274 } 275 ~TaskPool()276 ~TaskPool() 277 { 278 if (data != nullptr) 279 { 280 281 for(size_t idx = 0; idx < N; idx++) 282 { 283 PoolItem* pElement = Buffer() + idx; 284 285 int preValue = pElement->id.Exchange(TaskID::UNUSED); 286 if (preValue != TaskID::UNUSED) 287 { 288 pElement->task.~T(); 289 } 290 } 291 292 Memory::Free(data); 293 data = nullptr; 294 } 295 } 296 TryAlloc(T && task)297 TaskHandle TryAlloc(T && task) 298 { 299 int idx = index.IncFetch() - 1; 300 301 int clampedIdx = (idx & MASK); 302 303 PoolItem* pElement = Buffer() + clampedIdx; 304 305 bool isUnused = ((pElement->id.Load() & 1 ) != 0); 306 307 if (isUnused == false) 308 { 309 //Can't allocate more, next element in circular buffer is already used 310 return TaskHandle(); 311 } 312 313 //generate next even number for id 314 int id = idGenerator.AddFetch(2); 315 MoveCtor( pElement, id, std::move(task) ); 316 return TaskHandle(id, pElement); 317 } 318 319 Alloc(T && task)320 TaskHandle Alloc(T && task) 321 { 322 TaskHandle res = TryAlloc(std::move(task)); 323 MT_ASSERT(res.IsValid(), "Pool allocation failed"); 324 return res; 325 } 326 327 }; 328 329 } 330