1 /* 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #ifndef WEBRTC_COMMON_AUDIO_SWAP_QUEUE_H_ 12 #define WEBRTC_COMMON_AUDIO_SWAP_QUEUE_H_ 13 14 #include <algorithm> 15 #include <utility> 16 #include <vector> 17 18 #include "webrtc/base/checks.h" 19 #include "webrtc/base/criticalsection.h" 20 21 namespace webrtc { 22 23 namespace internal { 24 25 // (Internal; please don't use outside this file.) 26 template <typename T> 27 bool NoopSwapQueueItemVerifierFunction(const T&) { 28 return true; 29 } 30 31 } // namespace internal 32 33 // Functor to use when supplying a verifier function for the queue. 34 template <typename T, 35 bool (*QueueItemVerifierFunction)(const T&) = 36 internal::NoopSwapQueueItemVerifierFunction> 37 class SwapQueueItemVerifier { 38 public: 39 bool operator()(const T& t) const { return QueueItemVerifierFunction(t); } 40 }; 41 42 // This class is a fixed-size queue. A producer calls Insert() to insert 43 // an element of type T at the back of the queue, and a consumer calls 44 // Remove() to remove an element from the front of the queue. It's safe 45 // for the producer(s) and the consumer(s) to access the queue 46 // concurrently, from different threads. 47 // 48 // To avoid the construction, copying, and destruction of Ts that a naive 49 // queue implementation would require, for each "full" T passed from 50 // producer to consumer, SwapQueue<T> passes an "empty" T in the other 51 // direction (an "empty" T is one that contains nothing of value for the 52 // consumer). This bidirectional movement is implemented with swap(). 53 // 54 // // Create queue: 55 // Bottle proto(568); // Prepare an empty Bottle. Heap allocates space for 56 // // 568 ml. 57 // SwapQueue<Bottle> q(N, proto); // Init queue with N copies of proto. 58 // // Each copy allocates on the heap. 59 // // Producer pseudo-code: 60 // Bottle b(568); // Prepare an empty Bottle. Heap allocates space for 568 ml. 61 // loop { 62 // b.Fill(amount); // Where amount <= 568 ml. 63 // q.Insert(&b); // Swap our full Bottle for an empty one from q. 64 // } 65 // 66 // // Consumer pseudo-code: 67 // Bottle b(568); // Prepare an empty Bottle. Heap allocates space for 568 ml. 68 // loop { 69 // q.Remove(&b); // Swap our empty Bottle for the next-in-line full Bottle. 70 // Drink(&b); 71 // } 72 // 73 // For a well-behaved Bottle class, there are no allocations in the 74 // producer, since it just fills an empty Bottle that's already large 75 // enough; no deallocations in the consumer, since it returns each empty 76 // Bottle to the queue after having drunk it; and no copies along the 77 // way, since the queue uses swap() everywhere to move full Bottles in 78 // one direction and empty ones in the other. 79 template <typename T, typename QueueItemVerifier = SwapQueueItemVerifier<T>> 80 class SwapQueue { 81 public: 82 // Creates a queue of size size and fills it with default constructed Ts. 83 explicit SwapQueue(size_t size) : queue_(size) { 84 RTC_DCHECK(VerifyQueueSlots()); 85 } 86 87 // Same as above and accepts an item verification functor. 88 SwapQueue(size_t size, const QueueItemVerifier& queue_item_verifier) 89 : queue_item_verifier_(queue_item_verifier), queue_(size) { 90 RTC_DCHECK(VerifyQueueSlots()); 91 } 92 93 // Creates a queue of size size and fills it with copies of prototype. 94 SwapQueue(size_t size, const T& prototype) : queue_(size, prototype) { 95 RTC_DCHECK(VerifyQueueSlots()); 96 } 97 98 // Same as above and accepts an item verification functor. 99 SwapQueue(size_t size, 100 const T& prototype, 101 const QueueItemVerifier& queue_item_verifier) 102 : queue_item_verifier_(queue_item_verifier), queue_(size, prototype) { 103 RTC_DCHECK(VerifyQueueSlots()); 104 } 105 106 // Resets the queue to have zero content wile maintaining the queue size. 107 void Clear() { 108 rtc::CritScope cs(&crit_queue_); 109 next_write_index_ = 0; 110 next_read_index_ = 0; 111 num_elements_ = 0; 112 } 113 114 // Inserts a "full" T at the back of the queue by swapping *input with an 115 // "empty" T from the queue. 116 // Returns true if the item was inserted or false if not (the queue was full). 117 // When specified, the T given in *input must pass the ItemVerifier() test. 118 // The contents of *input after the call are then also guaranteed to pass the 119 // ItemVerifier() test. 120 bool Insert(T* input) WARN_UNUSED_RESULT { 121 RTC_DCHECK(input); 122 123 rtc::CritScope cs(&crit_queue_); 124 125 RTC_DCHECK(queue_item_verifier_(*input)); 126 127 if (num_elements_ == queue_.size()) { 128 return false; 129 } 130 131 using std::swap; 132 swap(*input, queue_[next_write_index_]); 133 134 ++next_write_index_; 135 if (next_write_index_ == queue_.size()) { 136 next_write_index_ = 0; 137 } 138 139 ++num_elements_; 140 141 RTC_DCHECK_LT(next_write_index_, queue_.size()); 142 RTC_DCHECK_LE(num_elements_, queue_.size()); 143 144 return true; 145 } 146 147 // Removes the frontmost "full" T from the queue by swapping it with 148 // the "empty" T in *output. 149 // Returns true if an item could be removed or false if not (the queue was 150 // empty). When specified, The T given in *output must pass the ItemVerifier() 151 // test and the contents of *output after the call are then also guaranteed to 152 // pass the ItemVerifier() test. 153 bool Remove(T* output) WARN_UNUSED_RESULT { 154 RTC_DCHECK(output); 155 156 rtc::CritScope cs(&crit_queue_); 157 158 RTC_DCHECK(queue_item_verifier_(*output)); 159 160 if (num_elements_ == 0) { 161 return false; 162 } 163 164 using std::swap; 165 swap(*output, queue_[next_read_index_]); 166 167 ++next_read_index_; 168 if (next_read_index_ == queue_.size()) { 169 next_read_index_ = 0; 170 } 171 172 --num_elements_; 173 174 RTC_DCHECK_LT(next_read_index_, queue_.size()); 175 RTC_DCHECK_LE(num_elements_, queue_.size()); 176 177 return true; 178 } 179 180 private: 181 // Verify that the queue slots complies with the ItemVerifier test. 182 bool VerifyQueueSlots() { 183 rtc::CritScope cs(&crit_queue_); 184 for (const auto& v : queue_) { 185 RTC_DCHECK(queue_item_verifier_(v)); 186 } 187 return true; 188 } 189 190 rtc::CriticalSection crit_queue_; 191 192 // TODO(peah): Change this to use std::function() once we can use C++11 std 193 // lib. 194 QueueItemVerifier queue_item_verifier_ GUARDED_BY(crit_queue_); 195 196 // (next_read_index_ + num_elements_) % queue_.size() = 197 // next_write_index_ 198 size_t next_write_index_ GUARDED_BY(crit_queue_) = 0; 199 size_t next_read_index_ GUARDED_BY(crit_queue_) = 0; 200 size_t num_elements_ GUARDED_BY(crit_queue_) = 0; 201 202 // queue_.size() is constant. 203 std::vector<T> queue_ GUARDED_BY(crit_queue_); 204 205 RTC_DISALLOW_COPY_AND_ASSIGN(SwapQueue); 206 }; 207 208 } // namespace webrtc 209 210 #endif // WEBRTC_COMMON_AUDIO_SWAP_QUEUE_H_ 211