1 /* 2 * Copyright 2019 The libgav1 Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef LIBGAV1_SRC_UTILS_UNBOUNDED_QUEUE_H_ 18 #define LIBGAV1_SRC_UTILS_UNBOUNDED_QUEUE_H_ 19 20 #include <cassert> 21 #include <cstddef> 22 #include <memory> 23 #include <new> 24 #include <utility> 25 26 #include "src/utils/compiler_attributes.h" 27 #include "src/utils/memory.h" 28 29 namespace libgav1 { 30 31 // A FIFO queue of an unbounded capacity. 32 // 33 // This implementation uses the general approach used in std::deque 34 // implementations. See, for example, 35 // https://stackoverflow.com/questions/6292332/what-really-is-a-deque-in-stl 36 // 37 // It is much simpler because it just needs to support the queue interface. 38 // The blocks are chained into a circular list, not managed by a "map". It 39 // does not shrink the internal buffer. 40 // 41 // An alternative implementation approach is a resizable circular array. See, 42 // for example, ResizingArrayQueue.java in https://algs4.cs.princeton.edu/code/ 43 // and base::circular_deque in Chromium's base/containers library. 44 template <typename T> 45 class UnboundedQueue { 46 public: 47 UnboundedQueue() = default; 48 49 // Move only. UnboundedQueue(UnboundedQueue && other)50 UnboundedQueue(UnboundedQueue&& other) 51 : first_block_(other.first_block_), 52 front_(other.front_), 53 last_block_(other.last_block_), 54 back_(other.back_) { 55 other.first_block_ = nullptr; 56 other.front_ = 0; 57 other.last_block_ = nullptr; 58 other.back_ = 0; 59 } 60 UnboundedQueue& operator=(UnboundedQueue&& other) { 61 if (this != &other) { 62 Destroy(); 63 first_block_ = other.first_block_; 64 front_ = other.front_; 65 last_block_ = other.last_block_; 66 back_ = other.back_; 67 other.first_block_ = nullptr; 68 other.front_ = 0; 69 other.last_block_ = nullptr; 70 other.back_ = 0; 71 } 72 return *this; 73 } 74 ~UnboundedQueue()75 ~UnboundedQueue() { Destroy(); } 76 77 // Allocates two Blocks upfront because most access patterns require at 78 // least two Blocks. Returns false if the allocation of the Blocks failed. Init()79 LIBGAV1_MUST_USE_RESULT bool Init() { 80 std::unique_ptr<Block> new_block0(new (std::nothrow) Block); 81 std::unique_ptr<Block> new_block1(new (std::nothrow) Block); 82 if (new_block0 == nullptr || new_block1 == nullptr) return false; 83 first_block_ = last_block_ = new_block0.release(); 84 new_block1->next = first_block_; 85 last_block_->next = new_block1.release(); 86 return true; 87 } 88 89 // Checks if the queue has room for a new element. If the queue is full, 90 // tries to grow it. Returns false if the queue is full and the attempt to 91 // grow it failed. 92 // 93 // NOTE: GrowIfNeeded() must be called before each call to Push(). This 94 // inconvenient design is necessary to guarantee a successful Push() call. 95 // 96 // Push(T&& value) is often called with the argument std::move(value). The 97 // moved-from object |value| won't be usable afterwards, so it would be 98 // problematic if Push(T&& value) failed and we lost access to the original 99 // |value| object. GrowIfNeeded()100 LIBGAV1_MUST_USE_RESULT bool GrowIfNeeded() { 101 assert(last_block_ != nullptr); 102 if (back_ == kBlockCapacity) { 103 if (last_block_->next == first_block_) { 104 // All Blocks are in use. 105 std::unique_ptr<Block> new_block(new (std::nothrow) Block); 106 if (new_block == nullptr) return false; 107 new_block->next = first_block_; 108 last_block_->next = new_block.release(); 109 } 110 last_block_ = last_block_->next; 111 back_ = 0; 112 } 113 return true; 114 } 115 116 // Pushes the element |value| to the end of the queue. It is an error to call 117 // Push() when the queue is full. Push(const T & value)118 void Push(const T& value) { 119 assert(last_block_ != nullptr); 120 assert(back_ < kBlockCapacity); 121 T* elements = reinterpret_cast<T*>(last_block_->buffer); 122 new (&elements[back_++]) T(value); 123 } 124 Push(T && value)125 void Push(T&& value) { 126 assert(last_block_ != nullptr); 127 assert(back_ < kBlockCapacity); 128 T* elements = reinterpret_cast<T*>(last_block_->buffer); 129 new (&elements[back_++]) T(std::move(value)); 130 } 131 132 // Returns the element at the front of the queue. It is an error to call 133 // Front() when the queue is empty. Front()134 T& Front() { 135 assert(!Empty()); 136 T* elements = reinterpret_cast<T*>(first_block_->buffer); 137 return elements[front_]; 138 } 139 Front()140 const T& Front() const { 141 assert(!Empty()); 142 T* elements = reinterpret_cast<T*>(first_block_->buffer); 143 return elements[front_]; 144 } 145 146 // Removes the element at the front of the queue from the queue. It is an 147 // error to call Pop() when the queue is empty. Pop()148 void Pop() { 149 assert(!Empty()); 150 T* elements = reinterpret_cast<T*>(first_block_->buffer); 151 elements[front_++].~T(); 152 if (front_ == kBlockCapacity) { 153 // The first block has become empty. 154 front_ = 0; 155 if (first_block_ == last_block_) { 156 // Only one Block is in use. Simply reset back_. 157 back_ = 0; 158 } else { 159 first_block_ = first_block_->next; 160 } 161 } 162 } 163 164 // Returns true if the queue is empty. Empty()165 bool Empty() const { return first_block_ == last_block_ && front_ == back_; } 166 167 private: 168 // kBlockCapacity is the maximum number of elements each Block can hold. 169 // sizeof(void*) is subtracted from 2048 to account for the |next| pointer in 170 // the Block struct. 171 // 172 // In Linux x86_64, sizeof(std::function<void()>) is 32, so each Block can 173 // hold 63 std::function<void()> objects. 174 // 175 // NOTE: The corresponding value in <deque> in libc++ revision 176 // 245b5ba3448b9d3f6de5962066557e253a6bc9a4 is: 177 // template <class _ValueType, class _DiffType> 178 // struct __deque_block_size { 179 // static const _DiffType value = 180 // sizeof(_ValueType) < 256 ? 4096 / sizeof(_ValueType) : 16; 181 // }; 182 // 183 // Note that 4096 / 256 = 16, so apparently this expression is intended to 184 // ensure the block size is at least 4096 bytes and each block can hold at 185 // least 16 elements. 186 static constexpr size_t kBlockCapacity = 187 (sizeof(T) < 128) ? (2048 - sizeof(void*)) / sizeof(T) : 16; 188 189 struct Block : public Allocable { 190 alignas(T) char buffer[kBlockCapacity * sizeof(T)]; 191 Block* next; 192 }; 193 Destroy()194 void Destroy() { 195 if (first_block_ == nullptr) return; // An uninitialized queue. 196 197 // First free the unused blocks, which are located after last_block and 198 // before first_block_. 199 Block* block = last_block_->next; 200 // Cut the circular list open after last_block_. 201 last_block_->next = nullptr; 202 while (block != first_block_) { 203 Block* next = block->next; 204 delete block; 205 block = next; 206 } 207 208 // Then free the used blocks. Destruct the elements in the used blocks. 209 while (block != nullptr) { 210 const size_t begin = (block == first_block_) ? front_ : 0; 211 const size_t end = (block == last_block_) ? back_ : kBlockCapacity; 212 T* elements = reinterpret_cast<T*>(block->buffer); 213 for (size_t i = begin; i < end; ++i) { 214 elements[i].~T(); 215 } 216 Block* next = block->next; 217 delete block; 218 block = next; 219 } 220 } 221 222 // Blocks are chained in a circular singly-linked list. If the list of Blocks 223 // is empty, both first_block_ and last_block_ are null pointers. If the list 224 // is nonempty, first_block_ points to the first used Block and last_block_ 225 // points to the last used Block. 226 // 227 // Invariant: If Init() is called and succeeds, the queue is always nonempty. 228 // This allows all methods (except the destructor) to avoid null pointer 229 // checks for first_block_ and last_block_. 230 Block* first_block_ = nullptr; 231 // The index of the element in first_block_ to be removed by Pop(). 232 size_t front_ = 0; 233 Block* last_block_ = nullptr; 234 // The index in last_block_ where the new element is inserted by Push(). 235 size_t back_ = 0; 236 }; 237 238 #if !LIBGAV1_CXX17 239 template <typename T> 240 constexpr size_t UnboundedQueue<T>::kBlockCapacity; 241 #endif 242 243 } // namespace libgav1 244 245 #endif // LIBGAV1_SRC_UTILS_UNBOUNDED_QUEUE_H_ 246