• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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 <list>
9 #include <vector>
10 
11 #include "base/basictypes.h"
12 #include "base/logging.h"
13 #include "base/threading/non_thread_safe.h"
14 #include "net/base/net_export.h"
15 
16 #if !defined(NDEBUG)
17 #include "base/containers/hash_tables.h"
18 #endif
19 
20 namespace net {
21 
22 // A simple priority queue. The order of values is by priority and then FIFO.
23 // Unlike the std::priority_queue, this implementation allows erasing elements
24 // from the queue, and all operations are O(p) time for p priority levels.
25 // The queue is agnostic to priority ordering (whether 0 precedes 1).
26 // If the highest priority is 0, FirstMin() returns the first in order.
27 //
28 // In debug-mode, the internal queues store (id, value) pairs where id is used
29 // to validate Pointers.
30 //
31 template<typename T>
32 class PriorityQueue : public base::NonThreadSafe {
33  private:
34   // This section is up-front for Pointer only.
35 #if !defined(NDEBUG)
36   typedef std::list<std::pair<unsigned, T> > List;
37 #else
38   typedef std::list<T> List;
39 #endif
40 
41  public:
42   typedef uint32 Priority;
43 
44   // A pointer to a value stored in the queue. The pointer becomes invalid
45   // when the queue is destroyed or cleared, or the value is erased.
46   class Pointer {
47    public:
48     // Constructs a null pointer.
Pointer()49     Pointer() : priority_(kNullPriority) {
50 #if !defined(NDEBUG)
51       id_ = static_cast<unsigned>(-1);
52 #endif
53       // TODO(syzm)
54       // An uninitialized iterator behaves like an uninitialized pointer as per
55       // the STL docs. The fix below is ugly and should possibly be replaced
56       // with a better approach.
57       iterator_ = dummy_empty_list_.end();
58     }
59 
Pointer(const Pointer & p)60     Pointer(const Pointer& p)
61         : priority_(p.priority_),
62           iterator_(p.iterator_) {
63 #if !defined(NDEBUG)
64       id_ = p.id_;
65 #endif
66     }
67 
68     Pointer& operator=(const Pointer& p) {
69       // Self-assignment is benign.
70       priority_ = p.priority_;
71       iterator_ = p.iterator_;
72 #if !defined(NDEBUG)
73       id_ = p.id_;
74 #endif
75       return *this;
76     }
77 
is_null()78     bool is_null() const { return priority_ == kNullPriority; }
79 
priority()80     Priority priority() const { return priority_; }
81 
82 #if !defined(NDEBUG)
value()83     const T& value() const { return iterator_->second; }
84 #else
value()85     const T& value() const { return *iterator_; }
86 #endif
87 
88     // Comparing to Pointer from a different PriorityQueue is undefined.
Equals(const Pointer & other)89     bool Equals(const Pointer& other) const {
90       return (priority_ == other.priority_) && (iterator_ == other.iterator_);
91     }
92 
Reset()93     void Reset() {
94       *this = Pointer();
95     }
96 
97    private:
98     friend class PriorityQueue;
99 
100     // Note that we need iterator and not const_iterator to pass to
101     // List::erase.  When C++11 is turned on for Chromium, this could
102     // be changed to const_iterator and the const_casts in the rest of
103     // the file can be removed.
104     typedef typename PriorityQueue::List::iterator ListIterator;
105 
106     static const Priority kNullPriority = static_cast<Priority>(-1);
107 
108     // It is guaranteed that Pointer will treat |iterator| as a
109     // const_iterator.
Pointer(Priority priority,const ListIterator & iterator)110     Pointer(Priority priority, const ListIterator& iterator)
111         : priority_(priority), iterator_(iterator) {
112 #if !defined(NDEBUG)
113       id_ = iterator_->first;
114 #endif
115     }
116 
117     Priority priority_;
118     ListIterator iterator_;
119     // The STL iterators when uninitialized are like uninitialized pointers
120     // which cause crashes when assigned to other iterators. We need to
121     // initialize a NULL iterator to the end of a valid list.
122     List dummy_empty_list_;
123 
124 #if !defined(NDEBUG)
125     // Used by the queue to check if a Pointer is valid.
126     unsigned id_;
127 #endif
128   };
129 
130   // Creates a new queue for |num_priorities|.
PriorityQueue(Priority num_priorities)131   explicit PriorityQueue(Priority num_priorities)
132       : lists_(num_priorities), size_(0) {
133 #if !defined(NDEBUG)
134     next_id_ = 0;
135 #endif
136   }
137 
138   // Adds |value| with |priority| to the queue. Returns a pointer to the
139   // created element.
Insert(const T & value,Priority priority)140   Pointer Insert(const T& value, Priority priority) {
141     DCHECK(CalledOnValidThread());
142     DCHECK_LT(priority, lists_.size());
143     ++size_;
144     List& list = lists_[priority];
145 #if !defined(NDEBUG)
146     unsigned id = next_id_;
147     valid_ids_.insert(id);
148     ++next_id_;
149     return Pointer(priority, list.insert(list.end(),
150                                          std::make_pair(id, value)));
151 #else
152     return Pointer(priority, list.insert(list.end(), value));
153 #endif
154   }
155 
156   // Adds |value| with |priority| to the queue. Returns a pointer to the
157   // created element.
InsertAtFront(const T & value,Priority priority)158   Pointer InsertAtFront(const T& value, Priority priority) {
159     DCHECK(CalledOnValidThread());
160     DCHECK_LT(priority, lists_.size());
161     ++size_;
162     List& list = lists_[priority];
163 #if !defined(NDEBUG)
164     unsigned id = next_id_;
165     valid_ids_.insert(id);
166     ++next_id_;
167     return Pointer(priority, list.insert(list.begin(),
168                                          std::make_pair(id, value)));
169 #else
170     return Pointer(priority, list.insert(list.begin(), value));
171 #endif
172   }
173 
174   // Removes the value pointed by |pointer| from the queue. All pointers to this
175   // value including |pointer| become invalid.
Erase(const Pointer & pointer)176   void Erase(const Pointer& pointer) {
177     DCHECK(CalledOnValidThread());
178     DCHECK_LT(pointer.priority_, lists_.size());
179     DCHECK_GT(size_, 0u);
180 
181 #if !defined(NDEBUG)
182     DCHECK_EQ(1u, valid_ids_.erase(pointer.id_));
183     DCHECK_EQ(pointer.iterator_->first, pointer.id_);
184 #endif
185 
186     --size_;
187     lists_[pointer.priority_].erase(pointer.iterator_);
188   }
189 
190   // Returns a pointer to the first value of minimum priority or a null-pointer
191   // if empty.
FirstMin()192   Pointer FirstMin() const {
193     DCHECK(CalledOnValidThread());
194     for (size_t i = 0; i < lists_.size(); ++i) {
195       List* list = const_cast<List*>(&lists_[i]);
196       if (!list->empty())
197         return Pointer(i, list->begin());
198     }
199     return Pointer();
200   }
201 
202   // Returns a pointer to the last value of minimum priority or a null-pointer
203   // if empty.
LastMin()204   Pointer LastMin() const {
205     DCHECK(CalledOnValidThread());
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->end());
210     }
211     return Pointer();
212   }
213 
214   // Returns a pointer to the first value of maximum priority or a null-pointer
215   // if empty.
FirstMax()216   Pointer FirstMax() const {
217     DCHECK(CalledOnValidThread());
218     for (size_t i = lists_.size(); i > 0; --i) {
219       size_t index = i - 1;
220       List* list = const_cast<List*>(&lists_[index]);
221       if (!list->empty())
222         return Pointer(index, list->begin());
223     }
224     return Pointer();
225   }
226 
227   // Returns a pointer to the last value of maximum priority or a null-pointer
228   // if empty.
LastMax()229   Pointer LastMax() const {
230     DCHECK(CalledOnValidThread());
231     for (size_t i = lists_.size(); i > 0; --i) {
232       size_t index = i - 1;
233       List* list = const_cast<List*>(&lists_[index]);
234       if (!list->empty())
235         return Pointer(index, --list->end());
236     }
237     return Pointer();
238   }
239 
240   // Given an ordering of the values in this queue by decreasing
241   // priority and then FIFO, returns a pointer to the value following
242   // the value of the given pointer (which must be non-NULL).
243   //
244   // (One could also implement GetNextTowardsFirstMin() [decreasing
245   // priority, then reverse FIFO] as well as
246   // GetNextTowards{First,Last}Max() [increasing priority, then
247   // {,reverse} FIFO].)
GetNextTowardsLastMin(const Pointer & pointer)248   Pointer GetNextTowardsLastMin(const Pointer& pointer) const {
249     DCHECK(CalledOnValidThread());
250     DCHECK(!pointer.is_null());
251     DCHECK_LT(pointer.priority_, lists_.size());
252 
253     typename Pointer::ListIterator it = pointer.iterator_;
254     Priority priority = pointer.priority_;
255     DCHECK(it != lists_[priority].end());
256     ++it;
257     while (it == lists_[priority].end()) {
258       if (priority == 0u)
259         return Pointer();
260       --priority;
261       it = const_cast<List*>(&lists_[priority])->begin();
262     }
263     return Pointer(priority, it);
264   }
265 
266   // Empties the queue. All pointers become invalid.
Clear()267   void Clear() {
268     DCHECK(CalledOnValidThread());
269     for (size_t i = 0; i < lists_.size(); ++i) {
270       lists_[i].clear();
271     }
272 #if !defined(NDEBUG)
273     valid_ids_.clear();
274 #endif
275     size_ = 0u;
276   }
277 
278   // Returns the number of priorities the queue supports.
num_priorities()279   size_t num_priorities() const {
280     DCHECK(CalledOnValidThread());
281     return lists_.size();
282   }
283 
empty()284   bool empty() const {
285     DCHECK(CalledOnValidThread());
286     return size_ == 0;
287   }
288 
289   // Returns number of queued values.
size()290   size_t size() const {
291     DCHECK(CalledOnValidThread());
292     return size_;
293   }
294 
295  private:
296   typedef std::vector<List> ListVector;
297 
298 #if !defined(NDEBUG)
299   unsigned next_id_;
300   base::hash_set<unsigned> valid_ids_;
301 #endif
302 
303   ListVector lists_;
304   size_t size_;
305 
306   DISALLOW_COPY_AND_ASSIGN(PriorityQueue);
307 };
308 
309 }  // namespace net
310 
311 #endif  // NET_BASE_PRIORITY_QUEUE_H_
312