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