1 /* 2 * Copyright (C) 2019 The Android Open Source Project 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 INCLUDE_PERFETTO_EXT_BASE_CIRCULAR_QUEUE_H_ 18 #define INCLUDE_PERFETTO_EXT_BASE_CIRCULAR_QUEUE_H_ 19 20 #include <stdint.h> 21 #include <iterator> 22 23 #include "perfetto/base/logging.h" 24 #include "perfetto/ext/base/utils.h" 25 26 namespace perfetto { 27 namespace base { 28 29 // CircularQueue is a push-back-only / pop-front-only queue with the following 30 // characteristics: 31 // - The storage is based on a flat circular buffer. Beginning and end wrap 32 // as necessary, to keep pushes and pops O(1) as long as capacity expansion is 33 // not required. 34 // - Capacity is automatically expanded like in a std::vector. Expansion has a 35 // O(N) cost. 36 // - It allows random access, allowing in-place std::sort. 37 // - Iterators are not stable. Mutating the container invalidates all iterators. 38 // - It doesn't bother with const-correctness. 39 // 40 // Implementation details: 41 // Internally, |begin|, |end| and iterators use 64-bit monotonic indexes, which 42 // are incremented as if the queue was backed by unlimited storage. 43 // Even assuming that elements are inserted and removed every nanosecond, 64 bit 44 // is enough for 584 years. 45 // Wrapping happens only when addressing elements in the underlying circular 46 // storage. This limits the complexity and avoiding dealing with modular 47 // arithmetic all over the places. 48 template <class T> 49 class CircularQueue { 50 public: 51 class Iterator { 52 public: 53 using difference_type = ptrdiff_t; 54 using value_type = T; 55 using pointer = T*; 56 using reference = T&; 57 using iterator_category = std::random_access_iterator_tag; 58 Iterator(CircularQueue * queue,uint64_t pos,uint32_t generation)59 Iterator(CircularQueue* queue, uint64_t pos, uint32_t generation) 60 : queue_(queue), 61 pos_(pos) 62 #if PERFETTO_DCHECK_IS_ON() 63 , 64 generation_(generation) 65 #endif 66 { 67 ignore_result(generation); 68 } 69 70 T* operator->() { 71 #if PERFETTO_DCHECK_IS_ON() 72 PERFETTO_DCHECK(generation_ == queue_->generation()); 73 #endif 74 return queue_->Get(pos_); 75 } 76 77 const T* operator->() const { 78 return const_cast<CircularQueue<T>::Iterator*>(this)->operator->(); 79 } 80 81 T& operator*() { return *(operator->()); } 82 const T& operator*() const { return *(operator->()); } 83 84 value_type& operator[](difference_type i) { return *(*this + i); } 85 86 const value_type& operator[](difference_type i) const { 87 return const_cast<CircularQueue<T>::Iterator&>(*this)[i]; 88 } 89 90 Iterator& operator++() { 91 Add(1); 92 return *this; 93 } 94 95 Iterator operator++(int) { 96 Iterator ret = *this; 97 Add(1); 98 return ret; 99 } 100 101 Iterator& operator--() { 102 Add(-1); 103 return *this; 104 } 105 106 Iterator operator--(int) { 107 Iterator ret = *this; 108 Add(-1); 109 return ret; 110 } 111 112 friend Iterator operator+(const Iterator& iter, difference_type offset) { 113 Iterator ret = iter; 114 ret.Add(offset); 115 return ret; 116 } 117 118 Iterator& operator+=(difference_type offset) { 119 Add(offset); 120 return *this; 121 } 122 123 friend Iterator operator-(const Iterator& iter, difference_type offset) { 124 Iterator ret = iter; 125 ret.Add(-offset); 126 return ret; 127 } 128 129 Iterator& operator-=(difference_type offset) { 130 Add(-offset); 131 return *this; 132 } 133 134 friend ptrdiff_t operator-(const Iterator& lhs, const Iterator& rhs) { 135 return static_cast<ptrdiff_t>(lhs.pos_) - 136 static_cast<ptrdiff_t>(rhs.pos_); 137 } 138 139 friend bool operator==(const Iterator& lhs, const Iterator& rhs) { 140 return lhs.pos_ == rhs.pos_; 141 } 142 143 friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { 144 return lhs.pos_ != rhs.pos_; 145 } 146 147 friend bool operator<(const Iterator& lhs, const Iterator& rhs) { 148 return lhs.pos_ < rhs.pos_; 149 } 150 151 friend bool operator<=(const Iterator& lhs, const Iterator& rhs) { 152 return lhs.pos_ <= rhs.pos_; 153 } 154 155 friend bool operator>(const Iterator& lhs, const Iterator& rhs) { 156 return lhs.pos_ > rhs.pos_; 157 } 158 159 friend bool operator>=(const Iterator& lhs, const Iterator& rhs) { 160 return lhs.pos_ >= rhs.pos_; 161 } 162 163 private: Add(difference_type offset)164 inline void Add(difference_type offset) { 165 pos_ = static_cast<uint64_t>(static_cast<difference_type>(pos_) + offset); 166 PERFETTO_DCHECK(pos_ <= queue_->end_); 167 } 168 169 CircularQueue* queue_; 170 uint64_t pos_; 171 172 #if PERFETTO_DCHECK_IS_ON() 173 uint32_t generation_; 174 #endif 175 }; 176 177 CircularQueue(size_t initial_capacity = 1024) { Grow(initial_capacity); } 178 CircularQueue(CircularQueue && other)179 CircularQueue(CircularQueue&& other) noexcept { 180 // Copy all fields using the (private) default copy assignment operator. 181 *this = other; 182 increment_generation(); 183 new (&other) CircularQueue(); // Reset the old queue so it's still usable. 184 } 185 186 CircularQueue& operator=(CircularQueue&& other) { 187 this->~CircularQueue(); // Destroy the current state. 188 new (this) CircularQueue(std::move(other)); // Use the move ctor above. 189 return *this; 190 } 191 ~CircularQueue()192 ~CircularQueue() { 193 if (!entries_) { 194 PERFETTO_DCHECK(empty()); 195 return; 196 } 197 clear(); // Invoke destructors on all alive entries. 198 PERFETTO_DCHECK(empty()); 199 free(entries_); 200 } 201 202 template <typename... Args> emplace_back(Args &&...args)203 void emplace_back(Args&&... args) { 204 increment_generation(); 205 if (PERFETTO_UNLIKELY(size() >= capacity_)) 206 Grow(); 207 T* slot = Get(end_++); 208 new (slot) T(std::forward<Args>(args)...); 209 } 210 erase_front(size_t n)211 void erase_front(size_t n) { 212 increment_generation(); 213 for (; n && (begin_ < end_); --n) { 214 Get(begin_)->~T(); 215 begin_++; // This needs to be its own statement, Get() checks begin_. 216 } 217 } 218 pop_front()219 void pop_front() { erase_front(1); } 220 clear()221 void clear() { erase_front(size()); } 222 at(size_t idx)223 T& at(size_t idx) { 224 PERFETTO_DCHECK(idx < size()); 225 return *Get(begin_ + idx); 226 } 227 begin()228 Iterator begin() { return Iterator(this, begin_, generation()); } end()229 Iterator end() { return Iterator(this, end_, generation()); } front()230 T& front() { return *begin(); } back()231 T& back() { return *(end() - 1); } 232 empty()233 bool empty() const { return size() == 0; } 234 size()235 size_t size() const { 236 PERFETTO_DCHECK(end_ - begin_ <= capacity_); 237 return static_cast<size_t>(end_ - begin_); 238 } 239 capacity()240 size_t capacity() const { return capacity_; } 241 242 #if PERFETTO_DCHECK_IS_ON() generation()243 uint32_t generation() const { return generation_; } increment_generation()244 void increment_generation() { ++generation_; } 245 #else generation()246 uint32_t generation() const { return 0; } increment_generation()247 void increment_generation() {} 248 #endif 249 250 private: 251 CircularQueue(const CircularQueue&) = delete; 252 CircularQueue& operator=(const CircularQueue&) = default; 253 254 void Grow(size_t new_capacity = 0) { 255 // Capacity must be always a power of two. This allows Get() to use a simple 256 // bitwise-AND for handling the wrapping instead of a full division. 257 new_capacity = new_capacity ? new_capacity : capacity_ * 2; 258 PERFETTO_CHECK((new_capacity & (new_capacity - 1)) == 0); // Must be pow2. 259 260 // On 32-bit systems this might hit the 4GB wall and overflow. We can't do 261 // anything other than crash in this case. 262 PERFETTO_CHECK(new_capacity > capacity_); 263 size_t malloc_size = new_capacity * sizeof(T); 264 PERFETTO_CHECK(malloc_size > new_capacity); 265 auto* new_vec = static_cast<T*>(malloc(malloc_size)); 266 267 // Move all elements in the expanded array. 268 size_t new_size = 0; 269 for (uint64_t i = begin_; i < end_; i++) 270 new (&new_vec[new_size++]) T(std::move(*Get(i))); // Placement move ctor. 271 272 // Even if all the elements are std::move()-d and likely empty, we are still 273 // required to call the dtor for them. 274 for (uint64_t i = begin_; i < end_; i++) 275 Get(i)->~T(); 276 free(entries_); // It's fine to free(nullptr) (for the ctor call case). 277 278 begin_ = 0; 279 end_ = new_size; 280 capacity_ = new_capacity; 281 entries_ = new_vec; 282 } 283 Get(uint64_t pos)284 inline T* Get(uint64_t pos) { 285 PERFETTO_DCHECK(pos >= begin_ && pos < end_); 286 PERFETTO_DCHECK((capacity_ & (capacity_ - 1)) == 0); // Must be a pow2. 287 auto index = static_cast<size_t>(pos & (capacity_ - 1)); 288 return &entries_[index]; 289 } 290 291 // Underlying storage. It's raw malloc-ed rather than being a unique_ptr<T[]> 292 // to allow having uninitialized entries inside it. 293 T* entries_ = nullptr; 294 size_t capacity_ = 0; // Number of allocated slots (NOT bytes) in |entries_|. 295 296 // The |begin_| and |end_| indexes are monotonic and never wrap. Modular arith 297 // is used only when dereferencing entries in the vector. 298 uint64_t begin_ = 0; 299 uint64_t end_ = 0; 300 301 // Generation is used in debug builds only for checking iterator validity. 302 #if PERFETTO_DCHECK_IS_ON() 303 uint32_t generation_ = 0; 304 #endif 305 }; 306 307 } // namespace base 308 } // namespace perfetto 309 310 #endif // INCLUDE_PERFETTO_EXT_BASE_CIRCULAR_QUEUE_H_ 311