@@ -51,66 +51,128 @@ class BlockingQueue {
5151
5252public:
5353 explicit BlockingQueue (int max_size=-1 )
54- : max_size_(max_size), halt_(false )
54+ : max_size_(max_size)
55+ , halt_(false )
56+ , adding_complete_(false )
5557 {
5658 }
5759
60+ /* *
61+ * Adds a copy of item to the queue. Blocks if queue is already full.
62+ * @param item object to be copied in to queue
63+ */
5864 void push (const T& item) {
5965 auto lock = acquire_lock ();
60- await_free_space (lock);
66+ wait_until_can_add (lock);
6167 queue_.push (item);
6268 cond_.notify_all ();
6369 }
6470
71+ /* *
72+ * Moves item in to the queue. Blocks if queue is already full.
73+ * @param item object to be moved in to queue
74+ */
6575 void push (T&& item) {
6676 auto lock = acquire_lock ();
67- await_free_space (lock);
77+ wait_until_can_add (lock);
6878 queue_.push (std::move (item));
6979 cond_.notify_all ();
7080 }
7181
82+ /* *
83+ * Constructs an instance of T in-place in the queue. Blocks if queue is already full.
84+ * @param args Arguments to be passed to T's constructor
85+ */
7286 template <typename ... Args>
7387 void emplace (Args&&... args) {
7488 auto lock = acquire_lock ();
75- await_free_space (lock);
89+ wait_until_can_add (lock);
7690 queue_.emplace (std::forward<Args>(args)...);
7791 cond_.notify_all ();
7892 }
7993
94+
95+ /* *
96+ * Moves an item out of the queue and returns it. Blocks if the queue is empty.
97+ * @return The object from the front of the queue
98+ */
8099 T pop () {
81100 auto lock = acquire_lock ();
82- await_non_empty (lock);
101+ wait_until_can_remove (lock);
83102 T result = std::move (queue_.front ());
84103 queue_.pop ();
85104 cond_.notify_all ();
86105 return result;
87106 }
88107
108+ /* *
109+ * Indicates that both producers and consumers should stop processing even if there are items
110+ * in the queue. Any future adds or removes will cause a QueueHaltedException to be thrown.
111+ * This can be used to prevent one side from blocking indefinitely when the other side fails.
112+ */
89113 void halt () {
90114 auto lock = acquire_lock ();
91115 halt_ = true ;
92116 cond_.notify_all ();
93117 }
94118
119+ /* *
120+ * Indicates that no more items will be added to the queue. Consumers should finish processing
121+ * items already in the queue. Any attempt to add items after this is called will cause a
122+ * QueueHaltedException to be thrown. This can be used to prevent consumers from blocking
123+ * indefinitely when a producer finishes.
124+ *
125+ * Loosely based on C#'s BlockingCollection<T>.CompleteAdding method
126+ * (https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.blockingcollection-1.completeadding?view=net-5.0#System_Collections_Concurrent_BlockingCollection_1_CompleteAdding).
127+ */
128+ void complete_adding () {
129+ auto lock = acquire_lock ();
130+ adding_complete_ = true ;
131+ cond_.notify_all ();
132+ }
133+
134+ /* *
135+ * @return true if adding complete. Queue may or may not be empty.
136+ */
137+ bool is_adding_complete () {
138+ auto lock = acquire_lock ();
139+ return halt_ || adding_complete_;
140+ }
141+
142+ /* *
143+ * The queue is "completed" if it has been halted or if a producer called complete_adding()
144+ * and the remaining items have all been removed from the queue.
145+ * @return true if items should not be added or removed from queue.
146+ */
147+ bool is_completed () {
148+ auto lock = acquire_lock ();
149+ return halt_ || (adding_complete_ && queue_.empty ());
150+ }
151+
152+
95153private:
96154 int max_size_;
97155 std::queue<T> queue_;
98156 std::mutex mutex_;
99157 std::condition_variable cond_;
100158 bool halt_;
159+ bool adding_complete_;
160+
101161
102- void await_non_empty (std::unique_lock<std::mutex> &lock) {
103- cond_.wait (lock, [this ] { return ( halt_ || !queue_.empty () ); });
104- if (halt_) {
162+ void wait_until_can_remove (std::unique_lock<std::mutex> &lock) {
163+ cond_.wait (lock, [this ] { return halt_ || adding_complete_ || !queue_.empty (); });
164+ if (halt_ || (queue_. empty () && adding_complete_) ) {
105165 throw QueueHaltedException ();
106166 }
107167 }
108168
109- void await_free_space (std::unique_lock<std::mutex> &lock) {
169+ void wait_until_can_add (std::unique_lock<std::mutex> &lock) {
110170 if (max_size_ > 0 ) {
111- cond_.wait (lock, [this ] { return (halt_ || queue_.size () < max_size_); });
171+ cond_.wait (lock, [this ] {
172+ return halt_ || adding_complete_ || queue_.size () < max_size_;
173+ });
112174 }
113- if (halt_) {
175+ if (halt_ || adding_complete_ ) {
114176 throw QueueHaltedException ();
115177 }
116178 }
0 commit comments