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 <stddef.h> 9 #include <stdint.h> 10 11 #include <list> 12 #include <utility> 13 #include <vector> 14 15 #include "base/bind.h" 16 #include "base/callback.h" 17 #include "base/check_op.h" 18 #include "base/threading/thread_checker.h" 19 20 #if !defined(NDEBUG) 21 #include <unordered_set> 22 #endif 23 24 namespace net { 25 26 // A simple priority queue. The order of values is by priority and then FIFO. 27 // Unlike the std::priority_queue, this implementation allows erasing elements 28 // from the queue, and all operations are O(p) time for p priority levels. 29 // The queue is agnostic to priority ordering (whether 0 precedes 1). 30 // If the highest priority is 0, FirstMin() returns the first in order. 31 // 32 // In debug-mode, the internal queues store (id, value) pairs where id is used 33 // to validate Pointers. 34 // 35 template <typename T> 36 class PriorityQueue { 37 private: 38 // This section is up-front for Pointer only. 39 #if !defined(NDEBUG) 40 typedef std::list<std::pair<unsigned, T> > List; 41 #else 42 typedef std::list<T> List; 43 #endif 44 45 public: 46 typedef uint32_t Priority; 47 48 // A pointer to a value stored in the queue. The pointer becomes invalid 49 // when the queue is destroyed or cleared, or the value is erased. 50 class Pointer { 51 public: 52 // Constructs a null pointer. Pointer()53 Pointer() : priority_(kNullPriority) { 54 #if !defined(NDEBUG) 55 id_ = static_cast<unsigned>(-1); 56 #endif 57 // TODO(syzm) 58 // An uninitialized iterator behaves like an uninitialized pointer as per 59 // the STL docs. The fix below is ugly and should possibly be replaced 60 // with a better approach. 61 iterator_ = dummy_empty_list_.end(); 62 } 63 Pointer(const Pointer & p)64 Pointer(const Pointer& p) 65 : priority_(p.priority_), 66 iterator_(p.iterator_) { 67 #if !defined(NDEBUG) 68 id_ = p.id_; 69 #endif 70 } 71 72 Pointer& operator=(const Pointer& p) { 73 // Self-assignment is benign. 74 priority_ = p.priority_; 75 iterator_ = p.iterator_; 76 #if !defined(NDEBUG) 77 id_ = p.id_; 78 #endif 79 return *this; 80 } 81 is_null()82 bool is_null() const { return priority_ == kNullPriority; } 83 priority()84 Priority priority() const { return priority_; } 85 86 #if !defined(NDEBUG) value()87 const T& value() const { return iterator_->second; } 88 #else value()89 const T& value() const { return *iterator_; } 90 #endif 91 92 // Comparing to Pointer from a different PriorityQueue is undefined. Equals(const Pointer & other)93 bool Equals(const Pointer& other) const { 94 return (priority_ == other.priority_) && (iterator_ == other.iterator_); 95 } 96 Reset()97 void Reset() { 98 *this = Pointer(); 99 } 100 101 private: 102 friend class PriorityQueue; 103 104 // Note that we need iterator and not const_iterator to pass to 105 // List::erase. When C++11 is turned on for Chromium, this could 106 // be changed to const_iterator and the const_casts in the rest of 107 // the file can be removed. 108 typedef typename PriorityQueue::List::iterator ListIterator; 109 110 static const Priority kNullPriority = static_cast<Priority>(-1); 111 112 // It is guaranteed that Pointer will treat |iterator| as a 113 // const_iterator. Pointer(Priority priority,const ListIterator & iterator)114 Pointer(Priority priority, const ListIterator& iterator) 115 : priority_(priority), iterator_(iterator) { 116 #if !defined(NDEBUG) 117 id_ = iterator_->first; 118 #endif 119 } 120 121 Priority priority_; 122 ListIterator iterator_; 123 // The STL iterators when uninitialized are like uninitialized pointers 124 // which cause crashes when assigned to other iterators. We need to 125 // initialize a NULL iterator to the end of a valid list. 126 List dummy_empty_list_; 127 128 #if !defined(NDEBUG) 129 // Used by the queue to check if a Pointer is valid. 130 unsigned id_; 131 #endif 132 }; 133 134 // Creates a new queue for |num_priorities|. PriorityQueue(Priority num_priorities)135 explicit PriorityQueue(Priority num_priorities) 136 : lists_(num_priorities), size_(0) { 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(std::make_pair(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::make_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 DCHECK(it != lists_[priority].end()); 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 DCHECK(it != lists_[priority].end()); 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_; 377 378 THREAD_CHECKER(thread_checker_); 379 }; 380 381 } // namespace net 382 383 #endif // NET_BASE_PRIORITY_QUEUE_H_ 384