• 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 <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