1d7cf17b1Ss.makeev_local // The MIT License (MIT) 2d7cf17b1Ss.makeev_local // 3d7cf17b1Ss.makeev_local // Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev 4d7cf17b1Ss.makeev_local // 5d7cf17b1Ss.makeev_local // Permission is hereby granted, free of charge, to any person obtaining a copy 6d7cf17b1Ss.makeev_local // of this software and associated documentation files (the "Software"), to deal 7d7cf17b1Ss.makeev_local // in the Software without restriction, including without limitation the rights 8d7cf17b1Ss.makeev_local // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9d7cf17b1Ss.makeev_local // copies of the Software, and to permit persons to whom the Software is 10d7cf17b1Ss.makeev_local // furnished to do so, subject to the following conditions: 11d7cf17b1Ss.makeev_local // 12d7cf17b1Ss.makeev_local // The above copyright notice and this permission notice shall be included in 13d7cf17b1Ss.makeev_local // all copies or substantial portions of the Software. 14d7cf17b1Ss.makeev_local // 15d7cf17b1Ss.makeev_local // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16d7cf17b1Ss.makeev_local // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17d7cf17b1Ss.makeev_local // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18d7cf17b1Ss.makeev_local // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19d7cf17b1Ss.makeev_local // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20d7cf17b1Ss.makeev_local // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21d7cf17b1Ss.makeev_local // THE SOFTWARE. 22d7cf17b1Ss.makeev_local 23d7cf17b1Ss.makeev_local #pragma once 24d7cf17b1Ss.makeev_local 25d7cf17b1Ss.makeev_local #include <vector> 26d7cf17b1Ss.makeev_local 27d7cf17b1Ss.makeev_local #include <MTPlatform.h> 28d7cf17b1Ss.makeev_local #include <MTTools.h> 29d7cf17b1Ss.makeev_local #include <MTAppInterop.h> 30d7cf17b1Ss.makeev_local 31d7cf17b1Ss.makeev_local 32d7cf17b1Ss.makeev_local namespace MT 33d7cf17b1Ss.makeev_local { 34b23bdf5aSs.makeev_local namespace TaskPriority 35b23bdf5aSs.makeev_local { 36b23bdf5aSs.makeev_local enum Type 37b23bdf5aSs.makeev_local { 38b23bdf5aSs.makeev_local HIGH = 0, 39b23bdf5aSs.makeev_local NORMAL = 1, 40b23bdf5aSs.makeev_local LOW = 2, 41b23bdf5aSs.makeev_local 42b23bdf5aSs.makeev_local COUNT, 43b23bdf5aSs.makeev_local 44b23bdf5aSs.makeev_local INVALID 45b23bdf5aSs.makeev_local }; 46b23bdf5aSs.makeev_local } 47b23bdf5aSs.makeev_local 48*3d930776Ss.makeev_local namespace DummyQueueFlag 49*3d930776Ss.makeev_local { 50*3d930776Ss.makeev_local enum Type 51*3d930776Ss.makeev_local { 52*3d930776Ss.makeev_local IS_DUMMY_QUEUE = 1 53*3d930776Ss.makeev_local }; 54*3d930776Ss.makeev_local } 55*3d930776Ss.makeev_local 56*3d930776Ss.makeev_local 57d7cf17b1Ss.makeev_local /// \class TaskQueue 58d7cf17b1Ss.makeev_local /// \brief thread safe task queue 59d7cf17b1Ss.makeev_local /// 60b23bdf5aSs.makeev_local template<typename T, uint32 CAPACITY> 61d7cf17b1Ss.makeev_local class TaskQueue 62d7cf17b1Ss.makeev_local { 63b23bdf5aSs.makeev_local ////////////////////////////////////////////////////////////////////////// 64b23bdf5aSs.makeev_local class Queue 65b23bdf5aSs.makeev_local { 66b23bdf5aSs.makeev_local static const int32 ALIGNMENT = 16; 67d7cf17b1Ss.makeev_local static const unsigned int MASK = CAPACITY - 1u; 68d7cf17b1Ss.makeev_local 69d7cf17b1Ss.makeev_local void* data; 70d7cf17b1Ss.makeev_local size_t begin; 71d7cf17b1Ss.makeev_local size_t end; 72d7cf17b1Ss.makeev_local Buffer()73d7cf17b1Ss.makeev_local inline T* Buffer() 74d7cf17b1Ss.makeev_local { 75d7cf17b1Ss.makeev_local return (T*)(data); 76d7cf17b1Ss.makeev_local } 77d7cf17b1Ss.makeev_local CopyCtor(T * element,const T & val)78d7cf17b1Ss.makeev_local inline void CopyCtor(T* element, const T & val) 79d7cf17b1Ss.makeev_local { 80d7cf17b1Ss.makeev_local new(element) T(val); 81d7cf17b1Ss.makeev_local } 82d7cf17b1Ss.makeev_local MoveCtor(T * element,T && val)83d7cf17b1Ss.makeev_local inline void MoveCtor(T* element, T && val) 84d7cf17b1Ss.makeev_local { 85d7cf17b1Ss.makeev_local new(element) T(std::move(val)); 86d7cf17b1Ss.makeev_local } 87d7cf17b1Ss.makeev_local Dtor(T * element)88d7cf17b1Ss.makeev_local inline void Dtor(T* element) 89d7cf17b1Ss.makeev_local { 90d7cf17b1Ss.makeev_local MT_UNUSED(element); 91d7cf17b1Ss.makeev_local element->~T(); 92d7cf17b1Ss.makeev_local } 93d7cf17b1Ss.makeev_local Size()94d7cf17b1Ss.makeev_local inline size_t Size() const 95d7cf17b1Ss.makeev_local { 96b23bdf5aSs.makeev_local if (IsEmpty()) 97d7cf17b1Ss.makeev_local { 98d7cf17b1Ss.makeev_local return 0; 99d7cf17b1Ss.makeev_local } 100d7cf17b1Ss.makeev_local 101d7cf17b1Ss.makeev_local size_t count = ((end & MASK) - (begin & MASK)) & MASK; 102d7cf17b1Ss.makeev_local return count; 103d7cf17b1Ss.makeev_local } 104d7cf17b1Ss.makeev_local Clear()105d7cf17b1Ss.makeev_local inline void Clear() 106d7cf17b1Ss.makeev_local { 107d7cf17b1Ss.makeev_local size_t queueSize = Size(); 108d7cf17b1Ss.makeev_local for (size_t i = 0; i < queueSize; i++) 109d7cf17b1Ss.makeev_local { 110d7cf17b1Ss.makeev_local T* pElement = Buffer() + ((begin + i) & MASK); 111d7cf17b1Ss.makeev_local Dtor(pElement); 112d7cf17b1Ss.makeev_local } 113d7cf17b1Ss.makeev_local 114d7cf17b1Ss.makeev_local begin = 0; 115d7cf17b1Ss.makeev_local end = 0; 116d7cf17b1Ss.makeev_local } 117d7cf17b1Ss.makeev_local 118d7cf17b1Ss.makeev_local public: 119d7cf17b1Ss.makeev_local Queue()120b23bdf5aSs.makeev_local Queue() 121*3d930776Ss.makeev_local : data(nullptr) 122*3d930776Ss.makeev_local , begin(0) 123d7cf17b1Ss.makeev_local , end(0) 124d7cf17b1Ss.makeev_local { 125*3d930776Ss.makeev_local } 126*3d930776Ss.makeev_local 127*3d930776Ss.makeev_local // Queue is just dummy until you call the Create Create()128*3d930776Ss.makeev_local void Create() 129*3d930776Ss.makeev_local { 130d7cf17b1Ss.makeev_local size_t bytesCount = sizeof(T) * CAPACITY; 131d7cf17b1Ss.makeev_local data = Memory::Alloc(bytesCount, ALIGNMENT); 132d7cf17b1Ss.makeev_local } 133d7cf17b1Ss.makeev_local 134*3d930776Ss.makeev_local ~Queue()135b23bdf5aSs.makeev_local ~Queue() 136d7cf17b1Ss.makeev_local { 137d7cf17b1Ss.makeev_local if (data != nullptr) 138d7cf17b1Ss.makeev_local { 139d7cf17b1Ss.makeev_local Memory::Free(data); 140d7cf17b1Ss.makeev_local data = nullptr; 141d7cf17b1Ss.makeev_local } 142d7cf17b1Ss.makeev_local } 143d7cf17b1Ss.makeev_local HasSpace(size_t itemCount)144b23bdf5aSs.makeev_local inline bool HasSpace(size_t itemCount) 145d7cf17b1Ss.makeev_local { 146b23bdf5aSs.makeev_local if ((Size() + itemCount) >= CAPACITY) 147d7cf17b1Ss.makeev_local { 148d7cf17b1Ss.makeev_local return false; 149d7cf17b1Ss.makeev_local } 150d7cf17b1Ss.makeev_local 151d7cf17b1Ss.makeev_local return true; 152d7cf17b1Ss.makeev_local } 153d7cf17b1Ss.makeev_local Add(const T & item)154b23bdf5aSs.makeev_local inline bool Add(const T& item) 155d7cf17b1Ss.makeev_local { 156*3d930776Ss.makeev_local MT_VERIFY(data, "Can't add items to dummy queue", return false; ); 157*3d930776Ss.makeev_local 158b23bdf5aSs.makeev_local if ((Size() + 1) >= CAPACITY) 159b23bdf5aSs.makeev_local { 160b23bdf5aSs.makeev_local return false; 161b23bdf5aSs.makeev_local } 162d7cf17b1Ss.makeev_local 163b23bdf5aSs.makeev_local size_t index = (end & MASK); 164b23bdf5aSs.makeev_local T* pElement = Buffer() + index; 165b23bdf5aSs.makeev_local CopyCtor( pElement, item ); 166b23bdf5aSs.makeev_local end++; 167b23bdf5aSs.makeev_local 168b23bdf5aSs.makeev_local return true; 169b23bdf5aSs.makeev_local } 170b23bdf5aSs.makeev_local 171b23bdf5aSs.makeev_local TryPopOldest(T & item)172b23bdf5aSs.makeev_local inline bool TryPopOldest(T & item) 173b23bdf5aSs.makeev_local { 174b23bdf5aSs.makeev_local if (IsEmpty()) 175d7cf17b1Ss.makeev_local { 176d7cf17b1Ss.makeev_local return false; 177d7cf17b1Ss.makeev_local } 178d7cf17b1Ss.makeev_local 179*3d930776Ss.makeev_local MT_VERIFY(data, "Can't pop items from dummy queue", return false; ); 180*3d930776Ss.makeev_local 181d7cf17b1Ss.makeev_local size_t index = (begin & MASK); 182d7cf17b1Ss.makeev_local T* pElement = Buffer() + index; 183d7cf17b1Ss.makeev_local begin++; 184d7cf17b1Ss.makeev_local item = *pElement; 185d7cf17b1Ss.makeev_local Dtor(pElement); 186d7cf17b1Ss.makeev_local return true; 187d7cf17b1Ss.makeev_local } 188d7cf17b1Ss.makeev_local TryPopNewest(T & item)189b23bdf5aSs.makeev_local inline bool TryPopNewest(T & item) 190d7cf17b1Ss.makeev_local { 191b23bdf5aSs.makeev_local if (IsEmpty()) 192d7cf17b1Ss.makeev_local { 193d7cf17b1Ss.makeev_local return false; 194d7cf17b1Ss.makeev_local } 195d7cf17b1Ss.makeev_local 196*3d930776Ss.makeev_local MT_VERIFY(data, "Can't pop items from dummy queue", return false; ); 197*3d930776Ss.makeev_local 198d7cf17b1Ss.makeev_local end--; 199d7cf17b1Ss.makeev_local size_t index = (end & MASK); 200d7cf17b1Ss.makeev_local T* pElement = Buffer() + index; 201d7cf17b1Ss.makeev_local item = *pElement; 202d7cf17b1Ss.makeev_local Dtor(pElement); 203d7cf17b1Ss.makeev_local return true; 204d7cf17b1Ss.makeev_local } 205d7cf17b1Ss.makeev_local IsEmpty()206b23bdf5aSs.makeev_local inline bool IsEmpty() const 207b23bdf5aSs.makeev_local { 208b23bdf5aSs.makeev_local return (begin == end); 209b23bdf5aSs.makeev_local } 210b23bdf5aSs.makeev_local }; 211b23bdf5aSs.makeev_local ////////////////////////////////////////////////////////////////////////// 212b23bdf5aSs.makeev_local 213b23bdf5aSs.makeev_local MT::Mutex mutex; 214b23bdf5aSs.makeev_local Queue queues[TaskPriority::COUNT]; 215b23bdf5aSs.makeev_local 216b23bdf5aSs.makeev_local public: 217b23bdf5aSs.makeev_local 218b23bdf5aSs.makeev_local MT_NOCOPYABLE(TaskQueue); 219b23bdf5aSs.makeev_local TaskQueue()220b23bdf5aSs.makeev_local TaskQueue() 221b23bdf5aSs.makeev_local { 222*3d930776Ss.makeev_local for(uint32 i = 0; i < MT_ARRAY_SIZE(queues); i++) 223*3d930776Ss.makeev_local { 224*3d930776Ss.makeev_local queues[i].Create(); 225*3d930776Ss.makeev_local } 226*3d930776Ss.makeev_local } 227*3d930776Ss.makeev_local TaskQueue(DummyQueueFlag::Type)228*3d930776Ss.makeev_local TaskQueue(DummyQueueFlag::Type) 229*3d930776Ss.makeev_local { 230*3d930776Ss.makeev_local // Create dummy queue. 231b23bdf5aSs.makeev_local } 232b23bdf5aSs.makeev_local ~TaskQueue()233b23bdf5aSs.makeev_local ~TaskQueue() 234b23bdf5aSs.makeev_local { 235b23bdf5aSs.makeev_local } 236b23bdf5aSs.makeev_local Add(const T * itemArray,size_t count)237b23bdf5aSs.makeev_local bool Add(const T* itemArray, size_t count) 238d7cf17b1Ss.makeev_local { 239d7cf17b1Ss.makeev_local MT::ScopedGuard guard(mutex); 240d7cf17b1Ss.makeev_local 241b23bdf5aSs.makeev_local // Check for space for all queues. 242b23bdf5aSs.makeev_local // At the moment it is not known exactly in what queue items will be added. 243b23bdf5aSs.makeev_local for(size_t i = 0; i < MT_ARRAY_SIZE(queues); i++) 244b23bdf5aSs.makeev_local { 245b23bdf5aSs.makeev_local Queue& queue = queues[i]; 246b23bdf5aSs.makeev_local if (!queue.HasSpace(count)) 247b23bdf5aSs.makeev_local { 248b23bdf5aSs.makeev_local return false; 249d7cf17b1Ss.makeev_local } 250b23bdf5aSs.makeev_local } 251b23bdf5aSs.makeev_local 252b23bdf5aSs.makeev_local // Adding the tasks into the appropriate queue 253b23bdf5aSs.makeev_local for(size_t i = 0; i < count; i++) 254b23bdf5aSs.makeev_local { 255b23bdf5aSs.makeev_local const T& item = itemArray[i]; 256b23bdf5aSs.makeev_local 257b23bdf5aSs.makeev_local uint32 queueIndex = (uint32)item.desc.priority; 258b23bdf5aSs.makeev_local MT_ASSERT(queueIndex < MT_ARRAY_SIZE(queues), "Invalid task priority"); 259b23bdf5aSs.makeev_local 260b23bdf5aSs.makeev_local Queue& queue = queues[queueIndex]; 261b23bdf5aSs.makeev_local bool res = queue.Add(itemArray[i]); 2620d87ba20Ss.makeev_local MT_USED_IN_ASSERT(res); 263b23bdf5aSs.makeev_local MT_ASSERT(res == true, "Sanity check failed"); 264b23bdf5aSs.makeev_local } 265b23bdf5aSs.makeev_local 266b23bdf5aSs.makeev_local return true; 267b23bdf5aSs.makeev_local } 268b23bdf5aSs.makeev_local 269b23bdf5aSs.makeev_local TryPopOldest(T & item)270b23bdf5aSs.makeev_local bool TryPopOldest(T & item) 271b23bdf5aSs.makeev_local { 272b23bdf5aSs.makeev_local MT::ScopedGuard guard(mutex); 273b23bdf5aSs.makeev_local for(uint32 queueIndex = 0; queueIndex < TaskPriority::COUNT; queueIndex++) 274b23bdf5aSs.makeev_local { 275b23bdf5aSs.makeev_local Queue& queue = queues[queueIndex]; 276b23bdf5aSs.makeev_local if (queue.TryPopOldest(item)) 277b23bdf5aSs.makeev_local { 278b23bdf5aSs.makeev_local return true; 279b23bdf5aSs.makeev_local } 280b23bdf5aSs.makeev_local } 281b23bdf5aSs.makeev_local return false; 282b23bdf5aSs.makeev_local } 283b23bdf5aSs.makeev_local TryPopNewest(T & item)284b23bdf5aSs.makeev_local bool TryPopNewest(T & item) 285b23bdf5aSs.makeev_local { 286b23bdf5aSs.makeev_local MT::ScopedGuard guard(mutex); 287b23bdf5aSs.makeev_local for(uint32 queueIndex = 0; queueIndex < TaskPriority::COUNT; queueIndex++) 288b23bdf5aSs.makeev_local { 289b23bdf5aSs.makeev_local Queue& queue = queues[queueIndex]; 290b23bdf5aSs.makeev_local if (queue.TryPopNewest(item)) 291b23bdf5aSs.makeev_local { 292b23bdf5aSs.makeev_local return true; 293b23bdf5aSs.makeev_local } 294b23bdf5aSs.makeev_local } 295b23bdf5aSs.makeev_local return false; 296b23bdf5aSs.makeev_local } 297b23bdf5aSs.makeev_local 298d7cf17b1Ss.makeev_local 299d7cf17b1Ss.makeev_local }; 300d7cf17b1Ss.makeev_local } 301