• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef NET_BASE_PRIORITY_QUEUE_H_
6 #define NET_BASE_PRIORITY_QUEUE_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <list>
12 #include <utility>
13 #include <vector>
14 
15 #include "base/check_op.h"
16 #include "base/functional/bind.h"
17 #include "base/functional/callback.h"
18 #include "base/not_fatal_until.h"
19 #include "base/threading/thread_checker.h"
20 
21 #if !defined(NDEBUG)
22 #include <unordered_set>
23 #endif
24 
25 namespace net {
26 
27 // A simple priority queue. The order of values is by priority and then FIFO.
28 // Unlike the std::priority_queue, this implementation allows erasing elements
29 // from the queue, and all operations are O(p) time for p priority levels.
30 // The queue is agnostic to priority ordering (whether 0 precedes 1).
31 // If the highest priority is 0, FirstMin() returns the first in order.
32 //
33 // In debug-mode, the internal queues store (id, value) pairs where id is used
34 // to validate Pointers.
35 //
36 template <typename T>
37 class PriorityQueue {
38  private:
39   // This section is up-front for Pointer only.
40 #if !defined(NDEBUG)
41   typedef std::list<std::pair<unsigned, T> > List;
42 #else
43   typedef std::list<T> List;
44 #endif
45 
46  public:
47   typedef uint32_t Priority;
48 
49   // A pointer to a value stored in the queue. The pointer becomes invalid
50   // when the queue is destroyed or cleared, or the value is erased.
51   class Pointer {
52    public:
53     // Constructs a null pointer.
Pointer()54     Pointer() : priority_(kNullPriority) {
55 #if !defined(NDEBUG)
56       id_ = static_cast<unsigned>(-1);
57 #endif
58       // TODO(syzm)
59       // An uninitialized iterator behaves like an uninitialized pointer as per
60       // the STL docs. The fix below is ugly and should possibly be replaced
61       // with a better approach.
62       iterator_ = dummy_empty_list_.end();
63     }
64 
Pointer(const Pointer & p)65     Pointer(const Pointer& p)
66         : priority_(p.priority_),
67           iterator_(p.iterator_) {
68 #if !defined(NDEBUG)
69       id_ = p.id_;
70 #endif
71     }
72 
73     Pointer& operator=(const Pointer& p) {
74       // Self-assignment is benign.
75       priority_ = p.priority_;
76       iterator_ = p.iterator_;
77 #if !defined(NDEBUG)
78       id_ = p.id_;
79 #endif
80       return *this;
81     }
82 
is_null()83     bool is_null() const { return priority_ == kNullPriority; }
84 
priority()85     Priority priority() const { return priority_; }
86 
87 #if !defined(NDEBUG)
value()88     const T& value() const { return iterator_->second; }
89 #else
value()90     const T& value() const { return *iterator_; }
91 #endif
92 
93     // Comparing to Pointer from a different PriorityQueue is undefined.
Equals(const Pointer & other)94     bool Equals(const Pointer& other) const {
95       return (priority_ == other.priority_) && (iterator_ == other.iterator_);
96     }
97 
Reset()98     void Reset() {
99       *this = Pointer();
100     }
101 
102    private:
103     friend class PriorityQueue;
104 
105     // Note that we need iterator and not const_iterator to pass to
106     // List::erase.  When C++11 is turned on for Chromium, this could
107     // be changed to const_iterator and the const_casts in the rest of
108     // the file can be removed.
109     typedef typename PriorityQueue::List::iterator ListIterator;
110 
111     static const Priority kNullPriority = static_cast<Priority>(-1);
112 
113     // It is guaranteed that Pointer will treat |iterator| as a
114     // const_iterator.
Pointer(Priority priority,const ListIterator & iterator)115     Pointer(Priority priority, const ListIterator& iterator)
116         : priority_(priority), iterator_(iterator) {
117 #if !defined(NDEBUG)
118       id_ = iterator_->first;
119 #endif
120     }
121 
122     Priority priority_;
123     ListIterator iterator_;
124     // The STL iterators when uninitialized are like uninitialized pointers
125     // which cause crashes when assigned to other iterators. We need to
126     // initialize a NULL iterator to the end of a valid list.
127     List dummy_empty_list_;
128 
129 #if !defined(NDEBUG)
130     // Used by the queue to check if a Pointer is valid.
131     unsigned id_;
132 #endif
133   };
134 
135   // Creates a new queue for |num_priorities|.
PriorityQueue(Priority num_priorities)136   explicit PriorityQueue(Priority num_priorities) : lists_(num_priorities) {
137 #if !defined(NDEBUG)
138     next_id_ = 0;
139 #endif
140   }
141 
142   PriorityQueue(const PriorityQueue&) = delete;
143   PriorityQueue& operator=(const PriorityQueue&) = delete;
~PriorityQueue()144   ~PriorityQueue() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); }
145 
146   // Adds |value| with |priority| to the queue. Returns a pointer to the
147   // created element.
Insert(T value,Priority priority)148   Pointer Insert(T value, Priority priority) {
149     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
150     DCHECK_LT(priority, lists_.size());
151     ++size_;
152     List& list = lists_[priority];
153 #if !defined(NDEBUG)
154     unsigned id = next_id_;
155     valid_ids_.insert(id);
156     ++next_id_;
157     list.emplace_back(id, std::move(value));
158 #else
159     list.emplace_back(std::move(value));
160 #endif
161     return Pointer(priority, std::prev(list.end()));
162   }
163 
164   // Adds |value| with |priority| to the queue. Returns a pointer to the
165   // created element.
InsertAtFront(T value,Priority priority)166   Pointer InsertAtFront(T value, Priority priority) {
167     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
168     DCHECK_LT(priority, lists_.size());
169     ++size_;
170     List& list = lists_[priority];
171 #if !defined(NDEBUG)
172     unsigned id = next_id_;
173     valid_ids_.insert(id);
174     ++next_id_;
175     list.emplace_front(std::pair(id, std::move(value)));
176 #else
177     list.emplace_front(std::move(value));
178 #endif
179     return Pointer(priority, list.begin());
180   }
181 
182   // Removes the value pointed by |pointer| from the queue. All pointers to this
183   // value including |pointer| become invalid. Returns the erased value.
Erase(const Pointer & pointer)184   T Erase(const Pointer& pointer) {
185     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
186     DCHECK_LT(pointer.priority_, lists_.size());
187     DCHECK_GT(size_, 0u);
188 
189 #if !defined(NDEBUG)
190     DCHECK_EQ(1u, valid_ids_.erase(pointer.id_));
191     DCHECK_EQ(pointer.iterator_->first, pointer.id_);
192     T erased = std::move(pointer.iterator_->second);
193 #else
194     T erased = std::move(*pointer.iterator_);
195 #endif
196 
197     --size_;
198     lists_[pointer.priority_].erase(pointer.iterator_);
199     return erased;
200   }
201 
202   // Returns a pointer to the first value of minimum priority or a null-pointer
203   // if empty.
FirstMin()204   Pointer FirstMin() const {
205     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
206     for (size_t i = 0; i < lists_.size(); ++i) {
207       List* list = const_cast<List*>(&lists_[i]);
208       if (!list->empty())
209         return Pointer(i, list->begin());
210     }
211     return Pointer();
212   }
213 
214   // Returns a pointer to the last value of minimum priority or a null-pointer
215   // if empty.
LastMin()216   Pointer LastMin() const {
217     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
218     for (size_t i = 0; i < lists_.size(); ++i) {
219       List* list = const_cast<List*>(&lists_[i]);
220       if (!list->empty())
221         return Pointer(i, --list->end());
222     }
223     return Pointer();
224   }
225 
226   // Returns a pointer to the first value of maximum priority or a null-pointer
227   // if empty.
FirstMax()228   Pointer FirstMax() const {
229     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
230     for (size_t i = lists_.size(); i > 0; --i) {
231       size_t index = i - 1;
232       List* list = const_cast<List*>(&lists_[index]);
233       if (!list->empty())
234         return Pointer(index, list->begin());
235     }
236     return Pointer();
237   }
238 
239   // Returns a pointer to the last value of maximum priority or a null-pointer
240   // if empty.
LastMax()241   Pointer LastMax() const {
242     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
243     for (size_t i = lists_.size(); i > 0; --i) {
244       size_t index = i - 1;
245       List* list = const_cast<List*>(&lists_[index]);
246       if (!list->empty())
247         return Pointer(index, --list->end());
248     }
249     return Pointer();
250   }
251 
252   // Given an ordering of the values in this queue by decreasing priority and
253   // then FIFO, returns a pointer to the value following the value of the given
254   // pointer (which must be non-NULL). I.e., gets the next element in decreasing
255   // priority, then FIFO order. If the given pointer is already pointing at the
256   // last value, returns a null Pointer.
257   //
258   // (One could also implement GetNextTowardsFirstMin() [decreasing priority,
259   // then reverse FIFO] as well as GetNextTowards{First,Last}Max() [increasing
260   // priority, then {,reverse} FIFO].)
GetNextTowardsLastMin(const Pointer & pointer)261   Pointer GetNextTowardsLastMin(const Pointer& pointer) const {
262     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
263     DCHECK(!pointer.is_null());
264     DCHECK_LT(pointer.priority_, lists_.size());
265 
266     typename Pointer::ListIterator it = pointer.iterator_;
267     Priority priority = pointer.priority_;
268     CHECK(it != lists_[priority].end(), base::NotFatalUntil::M130);
269     ++it;
270     while (it == lists_[priority].end()) {
271       if (priority == 0u) {
272         DCHECK(pointer.Equals(LastMin()));
273         return Pointer();
274       }
275       --priority;
276       it = const_cast<List*>(&lists_[priority])->begin();
277     }
278     return Pointer(priority, it);
279   }
280 
281   // Given an ordering of the values in this queue by decreasing priority and
282   // then FIFO, returns a pointer to the value preceding the value of the given
283   // pointer (which must be non-NULL). I.e., gets the next element in increasing
284   // priority, then reverse FIFO order. If the given pointer is already pointing
285   // at the first value, returns a null Pointer.
GetPreviousTowardsFirstMax(const Pointer & pointer)286   Pointer GetPreviousTowardsFirstMax(const Pointer& pointer) const {
287     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
288     DCHECK(!pointer.is_null());
289     DCHECK_LT(pointer.priority_, lists_.size());
290 
291     typename Pointer::ListIterator it = pointer.iterator_;
292     Priority priority = pointer.priority_;
293     CHECK(it != lists_[priority].end(), base::NotFatalUntil::M130);
294     while (it == lists_[priority].begin()) {
295       if (priority == num_priorities() - 1) {
296         DCHECK(pointer.Equals(FirstMax()));
297         return Pointer();
298       }
299       ++priority;
300       it = const_cast<List*>(&lists_[priority])->end();
301     }
302     return Pointer(priority, std::prev(it));
303   }
304 
305   // Checks whether |lhs| is closer in the queue to the first max element than
306   // |rhs|. Assumes that both Pointers refer to elements in this PriorityQueue.
IsCloserToFirstMaxThan(const Pointer & lhs,const Pointer & rhs)307   bool IsCloserToFirstMaxThan(const Pointer& lhs, const Pointer& rhs) {
308     if (lhs.Equals(rhs))
309       return false;
310     if (lhs.priority_ == rhs.priority_) {
311       // Traverse list starting from lhs and see if we find rhs.
312       for (auto it = lhs.iterator_; it != lists_[lhs.priority_].end(); ++it) {
313         if (it == rhs.iterator_)
314           return true;
315       }
316       return false;
317     }
318     return lhs.priority_ > rhs.priority_;
319   }
320 
321   // Checks whether |lhs| is closer in the queue to the last min element than
322   // |rhs|. Assumes that both Pointers refer to elements in this PriorityQueue.
IsCloserToLastMinThan(const Pointer & lhs,const Pointer & rhs)323   bool IsCloserToLastMinThan(const Pointer& lhs, const Pointer& rhs) {
324     return !lhs.Equals(rhs) && !IsCloserToFirstMaxThan(lhs, rhs);
325   }
326 
327   // Finds the first element (with respect to decreasing priority, then FIFO
328   // order) which matches the given predicate.
FindIf(const base::RepeatingCallback<bool (T)> & pred)329   Pointer FindIf(const base::RepeatingCallback<bool(T)>& pred) {
330     for (auto pointer = FirstMax(); !pointer.is_null();
331          pointer = GetNextTowardsLastMin(pointer)) {
332       if (pred.Run(pointer.value()))
333         return pointer;
334     }
335     return Pointer();
336   }
337 
338   // Empties the queue. All pointers become invalid.
Clear()339   void Clear() {
340     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
341     for (size_t i = 0; i < lists_.size(); ++i) {
342       lists_[i].clear();
343     }
344 #if !defined(NDEBUG)
345     valid_ids_.clear();
346 #endif
347     size_ = 0u;
348   }
349 
350   // Returns the number of priorities the queue supports.
num_priorities()351   size_t num_priorities() const {
352     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
353     return lists_.size();
354   }
355 
empty()356   bool empty() const {
357     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
358     return size_ == 0;
359   }
360 
361   // Returns number of queued values.
size()362   size_t size() const {
363     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
364     return size_;
365   }
366 
367  private:
368   typedef std::vector<List> ListVector;
369 
370 #if !defined(NDEBUG)
371   unsigned next_id_;
372   std::unordered_set<unsigned> valid_ids_;
373 #endif
374 
375   ListVector lists_;
376   size_t size_ = 0;
377 
378   THREAD_CHECKER(thread_checker_);
379 };
380 
381 }  // namespace net
382 
383 #endif  // NET_BASE_PRIORITY_QUEUE_H_
384