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/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) : lists_(num_priorities) { 136 #if !defined(NDEBUG) 137 next_id_ = 0; 138 #endif 139 } 140 141 PriorityQueue(const PriorityQueue&) = delete; 142 PriorityQueue& operator=(const PriorityQueue&) = delete; ~PriorityQueue()143 ~PriorityQueue() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); } 144 145 // Adds |value| with |priority| to the queue. Returns a pointer to the 146 // created element. Insert(T value,Priority priority)147 Pointer Insert(T value, Priority priority) { 148 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 149 DCHECK_LT(priority, lists_.size()); 150 ++size_; 151 List& list = lists_[priority]; 152 #if !defined(NDEBUG) 153 unsigned id = next_id_; 154 valid_ids_.insert(id); 155 ++next_id_; 156 list.emplace_back(id, std::move(value)); 157 #else 158 list.emplace_back(std::move(value)); 159 #endif 160 return Pointer(priority, std::prev(list.end())); 161 } 162 163 // Adds |value| with |priority| to the queue. Returns a pointer to the 164 // created element. InsertAtFront(T value,Priority priority)165 Pointer InsertAtFront(T value, Priority priority) { 166 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 167 DCHECK_LT(priority, lists_.size()); 168 ++size_; 169 List& list = lists_[priority]; 170 #if !defined(NDEBUG) 171 unsigned id = next_id_; 172 valid_ids_.insert(id); 173 ++next_id_; 174 list.emplace_front(std::pair(id, std::move(value))); 175 #else 176 list.emplace_front(std::move(value)); 177 #endif 178 return Pointer(priority, list.begin()); 179 } 180 181 // Removes the value pointed by |pointer| from the queue. All pointers to this 182 // value including |pointer| become invalid. Returns the erased value. Erase(const Pointer & pointer)183 T Erase(const Pointer& pointer) { 184 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 185 DCHECK_LT(pointer.priority_, lists_.size()); 186 DCHECK_GT(size_, 0u); 187 188 #if !defined(NDEBUG) 189 DCHECK_EQ(1u, valid_ids_.erase(pointer.id_)); 190 DCHECK_EQ(pointer.iterator_->first, pointer.id_); 191 T erased = std::move(pointer.iterator_->second); 192 #else 193 T erased = std::move(*pointer.iterator_); 194 #endif 195 196 --size_; 197 lists_[pointer.priority_].erase(pointer.iterator_); 198 return erased; 199 } 200 201 // Returns a pointer to the first value of minimum priority or a null-pointer 202 // if empty. FirstMin()203 Pointer FirstMin() const { 204 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 205 for (size_t i = 0; i < lists_.size(); ++i) { 206 List* list = const_cast<List*>(&lists_[i]); 207 if (!list->empty()) 208 return Pointer(i, list->begin()); 209 } 210 return Pointer(); 211 } 212 213 // Returns a pointer to the last value of minimum priority or a null-pointer 214 // if empty. LastMin()215 Pointer LastMin() const { 216 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 217 for (size_t i = 0; i < lists_.size(); ++i) { 218 List* list = const_cast<List*>(&lists_[i]); 219 if (!list->empty()) 220 return Pointer(i, --list->end()); 221 } 222 return Pointer(); 223 } 224 225 // Returns a pointer to the first value of maximum priority or a null-pointer 226 // if empty. FirstMax()227 Pointer FirstMax() const { 228 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 229 for (size_t i = lists_.size(); i > 0; --i) { 230 size_t index = i - 1; 231 List* list = const_cast<List*>(&lists_[index]); 232 if (!list->empty()) 233 return Pointer(index, list->begin()); 234 } 235 return Pointer(); 236 } 237 238 // Returns a pointer to the last value of maximum priority or a null-pointer 239 // if empty. LastMax()240 Pointer LastMax() const { 241 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 242 for (size_t i = lists_.size(); i > 0; --i) { 243 size_t index = i - 1; 244 List* list = const_cast<List*>(&lists_[index]); 245 if (!list->empty()) 246 return Pointer(index, --list->end()); 247 } 248 return Pointer(); 249 } 250 251 // Given an ordering of the values in this queue by decreasing priority and 252 // then FIFO, returns a pointer to the value following the value of the given 253 // pointer (which must be non-NULL). I.e., gets the next element in decreasing 254 // priority, then FIFO order. If the given pointer is already pointing at the 255 // last value, returns a null Pointer. 256 // 257 // (One could also implement GetNextTowardsFirstMin() [decreasing priority, 258 // then reverse FIFO] as well as GetNextTowards{First,Last}Max() [increasing 259 // priority, then {,reverse} FIFO].) GetNextTowardsLastMin(const Pointer & pointer)260 Pointer GetNextTowardsLastMin(const Pointer& pointer) const { 261 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 262 DCHECK(!pointer.is_null()); 263 DCHECK_LT(pointer.priority_, lists_.size()); 264 265 typename Pointer::ListIterator it = pointer.iterator_; 266 Priority priority = pointer.priority_; 267 DCHECK(it != lists_[priority].end()); 268 ++it; 269 while (it == lists_[priority].end()) { 270 if (priority == 0u) { 271 DCHECK(pointer.Equals(LastMin())); 272 return Pointer(); 273 } 274 --priority; 275 it = const_cast<List*>(&lists_[priority])->begin(); 276 } 277 return Pointer(priority, it); 278 } 279 280 // Given an ordering of the values in this queue by decreasing priority and 281 // then FIFO, returns a pointer to the value preceding the value of the given 282 // pointer (which must be non-NULL). I.e., gets the next element in increasing 283 // priority, then reverse FIFO order. If the given pointer is already pointing 284 // at the first value, returns a null Pointer. GetPreviousTowardsFirstMax(const Pointer & pointer)285 Pointer GetPreviousTowardsFirstMax(const Pointer& pointer) const { 286 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 287 DCHECK(!pointer.is_null()); 288 DCHECK_LT(pointer.priority_, lists_.size()); 289 290 typename Pointer::ListIterator it = pointer.iterator_; 291 Priority priority = pointer.priority_; 292 DCHECK(it != lists_[priority].end()); 293 while (it == lists_[priority].begin()) { 294 if (priority == num_priorities() - 1) { 295 DCHECK(pointer.Equals(FirstMax())); 296 return Pointer(); 297 } 298 ++priority; 299 it = const_cast<List*>(&lists_[priority])->end(); 300 } 301 return Pointer(priority, std::prev(it)); 302 } 303 304 // Checks whether |lhs| is closer in the queue to the first max element than 305 // |rhs|. Assumes that both Pointers refer to elements in this PriorityQueue. IsCloserToFirstMaxThan(const Pointer & lhs,const Pointer & rhs)306 bool IsCloserToFirstMaxThan(const Pointer& lhs, const Pointer& rhs) { 307 if (lhs.Equals(rhs)) 308 return false; 309 if (lhs.priority_ == rhs.priority_) { 310 // Traverse list starting from lhs and see if we find rhs. 311 for (auto it = lhs.iterator_; it != lists_[lhs.priority_].end(); ++it) { 312 if (it == rhs.iterator_) 313 return true; 314 } 315 return false; 316 } 317 return lhs.priority_ > rhs.priority_; 318 } 319 320 // Checks whether |lhs| is closer in the queue to the last min element than 321 // |rhs|. Assumes that both Pointers refer to elements in this PriorityQueue. IsCloserToLastMinThan(const Pointer & lhs,const Pointer & rhs)322 bool IsCloserToLastMinThan(const Pointer& lhs, const Pointer& rhs) { 323 return !lhs.Equals(rhs) && !IsCloserToFirstMaxThan(lhs, rhs); 324 } 325 326 // Finds the first element (with respect to decreasing priority, then FIFO 327 // order) which matches the given predicate. FindIf(const base::RepeatingCallback<bool (T)> & pred)328 Pointer FindIf(const base::RepeatingCallback<bool(T)>& pred) { 329 for (auto pointer = FirstMax(); !pointer.is_null(); 330 pointer = GetNextTowardsLastMin(pointer)) { 331 if (pred.Run(pointer.value())) 332 return pointer; 333 } 334 return Pointer(); 335 } 336 337 // Empties the queue. All pointers become invalid. Clear()338 void Clear() { 339 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 340 for (size_t i = 0; i < lists_.size(); ++i) { 341 lists_[i].clear(); 342 } 343 #if !defined(NDEBUG) 344 valid_ids_.clear(); 345 #endif 346 size_ = 0u; 347 } 348 349 // Returns the number of priorities the queue supports. num_priorities()350 size_t num_priorities() const { 351 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 352 return lists_.size(); 353 } 354 empty()355 bool empty() const { 356 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 357 return size_ == 0; 358 } 359 360 // Returns number of queued values. size()361 size_t size() const { 362 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); 363 return size_; 364 } 365 366 private: 367 typedef std::vector<List> ListVector; 368 369 #if !defined(NDEBUG) 370 unsigned next_id_; 371 std::unordered_set<unsigned> valid_ids_; 372 #endif 373 374 ListVector lists_; 375 size_t size_ = 0; 376 377 THREAD_CHECKER(thread_checker_); 378 }; 379 380 } // namespace net 381 382 #endif // NET_BASE_PRIORITY_QUEUE_H_ 383