3717 |
20 Nov 17 |
peter |
1 |
#ifndef theplu_yat_utility_basic_queue |
3717 |
20 Nov 17 |
peter |
2 |
#define theplu_yat_utility_basic_queue |
3717 |
20 Nov 17 |
peter |
3 |
|
3717 |
20 Nov 17 |
peter |
// $Id$ |
3717 |
20 Nov 17 |
peter |
5 |
|
4346 |
24 Apr 23 |
peter |
6 |
/* |
4346 |
24 Apr 23 |
peter |
Copyright (C) 2017, 2018, 2020, 2021, 2023 Peter Johansson |
4346 |
24 Apr 23 |
peter |
8 |
|
4346 |
24 Apr 23 |
peter |
This file is part of the yat library, https://dev.thep.lu.se/yat |
4346 |
24 Apr 23 |
peter |
10 |
|
4346 |
24 Apr 23 |
peter |
The yat library is free software; you can redistribute it and/or |
4346 |
24 Apr 23 |
peter |
modify it under the terms of the GNU General Public License as |
4346 |
24 Apr 23 |
peter |
published by the Free Software Foundation; either version 3 of the |
4346 |
24 Apr 23 |
peter |
License, or (at your option) any later version. |
4346 |
24 Apr 23 |
peter |
15 |
|
4346 |
24 Apr 23 |
peter |
The yat library is distributed in the hope that it will be useful, |
4346 |
24 Apr 23 |
peter |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
4346 |
24 Apr 23 |
peter |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
4346 |
24 Apr 23 |
peter |
General Public License for more details. |
4346 |
24 Apr 23 |
peter |
20 |
|
4346 |
24 Apr 23 |
peter |
You should have received a copy of the GNU General Public License |
4346 |
24 Apr 23 |
peter |
along with yat. If not, see <https://www.gnu.org/licenses/>. |
4346 |
24 Apr 23 |
peter |
23 |
*/ |
4346 |
24 Apr 23 |
peter |
24 |
|
3721 |
21 Nov 17 |
peter |
25 |
#include "config_public.h" |
3721 |
21 Nov 17 |
peter |
26 |
|
3946 |
20 Jul 20 |
peter |
27 |
#include <condition_variable> |
4043 |
01 Mar 21 |
peter |
28 |
#include <limits> |
3946 |
20 Jul 20 |
peter |
29 |
#include <mutex> |
3717 |
20 Nov 17 |
peter |
30 |
|
3717 |
20 Nov 17 |
peter |
31 |
namespace theplu { |
3717 |
20 Nov 17 |
peter |
32 |
namespace yat { |
3717 |
20 Nov 17 |
peter |
33 |
namespace utility { |
3717 |
20 Nov 17 |
peter |
34 |
namespace detail { |
3717 |
20 Nov 17 |
peter |
35 |
|
3717 |
20 Nov 17 |
peter |
36 |
/** |
3717 |
20 Nov 17 |
peter |
\internal Base class for Queue and PriorityQueue |
3717 |
20 Nov 17 |
peter |
38 |
*/ |
3717 |
20 Nov 17 |
peter |
39 |
template<class Derived, typename T, class Container> |
3717 |
20 Nov 17 |
peter |
40 |
class BasicQueue |
3717 |
20 Nov 17 |
peter |
41 |
{ |
3717 |
20 Nov 17 |
peter |
42 |
public: |
3717 |
20 Nov 17 |
peter |
/// Type of object stored |
3717 |
20 Nov 17 |
peter |
44 |
typedef typename Container::value_type value_type; |
3717 |
20 Nov 17 |
peter |
45 |
|
3717 |
20 Nov 17 |
peter |
46 |
/** |
3717 |
20 Nov 17 |
peter |
An unsigned integral type. \see size(void) |
3717 |
20 Nov 17 |
peter |
48 |
*/ |
3717 |
20 Nov 17 |
peter |
49 |
typedef typename Container::size_type size_type; |
3717 |
20 Nov 17 |
peter |
50 |
|
3717 |
20 Nov 17 |
peter |
51 |
/** |
3717 |
20 Nov 17 |
peter |
Default constructor |
3717 |
20 Nov 17 |
peter |
53 |
*/ |
4043 |
01 Mar 21 |
peter |
54 |
BasicQueue(void) |
4043 |
01 Mar 21 |
peter |
55 |
: capacity_(std::numeric_limits<size_t>::max()) |
4043 |
01 Mar 21 |
peter |
56 |
{} |
3717 |
20 Nov 17 |
peter |
57 |
|
3717 |
20 Nov 17 |
peter |
58 |
/** |
3717 |
20 Nov 17 |
peter |
Copy constructor |
3717 |
20 Nov 17 |
peter |
60 |
*/ |
3719 |
21 Nov 17 |
peter |
61 |
BasicQueue(const BasicQueue& other) |
3719 |
21 Nov 17 |
peter |
62 |
{ |
3946 |
20 Jul 20 |
peter |
63 |
std::unique_lock<std::mutex> lock(other.mutex_); |
3719 |
21 Nov 17 |
peter |
64 |
q_ = other.q_; |
4043 |
01 Mar 21 |
peter |
65 |
capacity_ = other.capacity_; |
3719 |
21 Nov 17 |
peter |
66 |
} // lock is released here |
3717 |
20 Nov 17 |
peter |
67 |
|
3717 |
20 Nov 17 |
peter |
68 |
/** |
3717 |
20 Nov 17 |
peter |
Construct queue from underlying Container |
3717 |
20 Nov 17 |
peter |
70 |
*/ |
4043 |
01 Mar 21 |
peter |
71 |
explicit BasicQueue(const Container& container) |
4043 |
01 Mar 21 |
peter |
72 |
: q_(container), capacity_(std::numeric_limits<size_t>::max()) |
4043 |
01 Mar 21 |
peter |
73 |
{} |
3717 |
20 Nov 17 |
peter |
74 |
|
4043 |
01 Mar 21 |
peter |
75 |
|
3717 |
20 Nov 17 |
peter |
76 |
/** |
4043 |
01 Mar 21 |
peter |
\return maximal number of element stored in container |
4043 |
01 Mar 21 |
peter |
78 |
|
4043 |
01 Mar 21 |
peter |
\since New in yat 0.19 |
4043 |
01 Mar 21 |
peter |
80 |
*/ |
4043 |
01 Mar 21 |
peter |
81 |
size_t capacity(void) |
4043 |
01 Mar 21 |
peter |
82 |
{ |
4043 |
01 Mar 21 |
peter |
83 |
std::unique_lock<std::mutex> lock(mutex_); |
4043 |
01 Mar 21 |
peter |
84 |
return capacity_; |
4043 |
01 Mar 21 |
peter |
85 |
} |
4043 |
01 Mar 21 |
peter |
86 |
|
4043 |
01 Mar 21 |
peter |
87 |
|
4043 |
01 Mar 21 |
peter |
88 |
/** |
4043 |
01 Mar 21 |
peter |
\brief change maximal number of element stored in container |
4043 |
01 Mar 21 |
peter |
90 |
|
4043 |
01 Mar 21 |
peter |
\since New in yat 0.19 |
4043 |
01 Mar 21 |
peter |
92 |
*/ |
4043 |
01 Mar 21 |
peter |
93 |
void capacity(size_t c) |
4043 |
01 Mar 21 |
peter |
94 |
{ |
4043 |
01 Mar 21 |
peter |
95 |
std::unique_lock<std::mutex> lock(mutex_); |
4043 |
01 Mar 21 |
peter |
96 |
capacity_ = c; |
4043 |
01 Mar 21 |
peter |
97 |
|
4043 |
01 Mar 21 |
peter |
98 |
lock.unlock(); |
4043 |
01 Mar 21 |
peter |
// Notify pushers that there is potentially space |
4043 |
01 Mar 21 |
peter |
100 |
push_condition_.notify_all(); |
4043 |
01 Mar 21 |
peter |
101 |
} |
4043 |
01 Mar 21 |
peter |
102 |
|
4043 |
01 Mar 21 |
peter |
103 |
/** |
3717 |
20 Nov 17 |
peter |
\brief clear queue |
3717 |
20 Nov 17 |
peter |
105 |
|
3717 |
20 Nov 17 |
peter |
\since new in yat 0.15 |
3717 |
20 Nov 17 |
peter |
107 |
*/ |
3717 |
20 Nov 17 |
peter |
108 |
void clear(void) |
3717 |
20 Nov 17 |
peter |
109 |
{ |
3946 |
20 Jul 20 |
peter |
110 |
std::unique_lock<std::mutex> lock(mutex_); |
4043 |
01 Mar 21 |
peter |
111 |
q_.clear(); |
4043 |
01 Mar 21 |
peter |
112 |
lock.unlock(); // unlock the mutex |
4043 |
01 Mar 21 |
peter |
113 |
|
4043 |
01 Mar 21 |
peter |
// Notify pushers that there is space to push |
4043 |
01 Mar 21 |
peter |
115 |
push_condition_.notify_all(); |
3717 |
20 Nov 17 |
peter |
116 |
} |
3717 |
20 Nov 17 |
peter |
117 |
|
3717 |
20 Nov 17 |
peter |
118 |
|
3717 |
20 Nov 17 |
peter |
119 |
/** |
3717 |
20 Nov 17 |
peter |
\return \c true if container's size is zero |
3717 |
20 Nov 17 |
peter |
121 |
*/ |
3717 |
20 Nov 17 |
peter |
122 |
bool empty(void) const |
3717 |
20 Nov 17 |
peter |
123 |
{ |
3946 |
20 Jul 20 |
peter |
124 |
std::unique_lock<std::mutex> lock(mutex_); |
3717 |
20 Nov 17 |
peter |
125 |
return q_.empty(); |
3717 |
20 Nov 17 |
peter |
126 |
} // lock is released here |
3717 |
20 Nov 17 |
peter |
127 |
|
3717 |
20 Nov 17 |
peter |
128 |
|
3717 |
20 Nov 17 |
peter |
129 |
/** |
3717 |
20 Nov 17 |
peter |
\brief access next element in queue |
3717 |
20 Nov 17 |
peter |
131 |
|
3717 |
20 Nov 17 |
peter |
Access the next element is queue. If container is empty, |
3717 |
20 Nov 17 |
peter |
process is waiting until other process is inserting element |
3717 |
20 Nov 17 |
peter |
into container. |
3717 |
20 Nov 17 |
peter |
135 |
*/ |
3717 |
20 Nov 17 |
peter |
136 |
void pop(T& value) |
3717 |
20 Nov 17 |
peter |
137 |
{ |
3946 |
20 Jul 20 |
peter |
138 |
std::unique_lock<std::mutex> lock(mutex_); |
3717 |
20 Nov 17 |
peter |
139 |
while (q_.empty()) |
4043 |
01 Mar 21 |
peter |
140 |
pop_condition_.wait(lock); |
3717 |
20 Nov 17 |
peter |
// The obvious choice would be to create a temp copy of front, |
3717 |
20 Nov 17 |
peter |
// pop the queue and then return by-value. This is, however, |
3717 |
20 Nov 17 |
peter |
// dangerous becasue if the copy constructor throws, the queue |
3717 |
20 Nov 17 |
peter |
// has been popped and the element is lost. Instead we choose to |
3717 |
20 Nov 17 |
peter |
// pass via passed reference. |
3717 |
20 Nov 17 |
peter |
146 |
static_cast<Derived*>(this)->pop_impl(value, lock); |
4043 |
01 Mar 21 |
peter |
147 |
lock.unlock(); // unlock the mutex |
4043 |
01 Mar 21 |
peter |
148 |
push_condition_.notify_one(); |
3717 |
20 Nov 17 |
peter |
149 |
} // lock is released here |
3717 |
20 Nov 17 |
peter |
150 |
|
3717 |
20 Nov 17 |
peter |
151 |
|
3717 |
20 Nov 17 |
peter |
152 |
/** |
3717 |
20 Nov 17 |
peter |
\brief insert an element into container |
4043 |
01 Mar 21 |
peter |
154 |
|
4043 |
01 Mar 21 |
peter |
If size of queue is equal (or greater) to its capacity, the |
4043 |
01 Mar 21 |
peter |
function is waiting until this is not the case. |
3717 |
20 Nov 17 |
peter |
157 |
*/ |
3717 |
20 Nov 17 |
peter |
158 |
void push(const T& t) |
3717 |
20 Nov 17 |
peter |
159 |
{ |
3946 |
20 Jul 20 |
peter |
160 |
std::unique_lock<std::mutex> lock(mutex_); |
4043 |
01 Mar 21 |
peter |
161 |
while (q_.size() >= capacity_) |
4043 |
01 Mar 21 |
peter |
162 |
push_condition_.wait(lock); |
3717 |
20 Nov 17 |
peter |
163 |
static_cast<Derived*>(this)->push_impl(t, lock); |
3717 |
20 Nov 17 |
peter |
164 |
lock.unlock(); // unlock the mutex |
3717 |
20 Nov 17 |
peter |
165 |
|
3717 |
20 Nov 17 |
peter |
// Notify others that data is ready after we have unlocked |
4043 |
01 Mar 21 |
peter |
167 |
pop_condition_.notify_one(); |
3717 |
20 Nov 17 |
peter |
168 |
} |
3717 |
20 Nov 17 |
peter |
169 |
|
3717 |
20 Nov 17 |
peter |
170 |
|
3717 |
20 Nov 17 |
peter |
171 |
/** |
3717 |
20 Nov 17 |
peter |
\brief insert an element into container |
3717 |
20 Nov 17 |
peter |
173 |
|
3717 |
20 Nov 17 |
peter |
\since New in yat 0.15 |
3717 |
20 Nov 17 |
peter |
175 |
*/ |
3717 |
20 Nov 17 |
peter |
176 |
void push(T&& t) |
3717 |
20 Nov 17 |
peter |
177 |
{ |
3946 |
20 Jul 20 |
peter |
178 |
std::unique_lock<std::mutex> lock(mutex_); |
4043 |
01 Mar 21 |
peter |
179 |
while (q_.size() >= capacity_) |
4043 |
01 Mar 21 |
peter |
180 |
push_condition_.wait(lock); |
3717 |
20 Nov 17 |
peter |
181 |
static_cast<Derived*>(this)->push_impl(std::move(t), lock); |
3717 |
20 Nov 17 |
peter |
182 |
lock.unlock(); // unlock the mutex |
3717 |
20 Nov 17 |
peter |
183 |
|
3717 |
20 Nov 17 |
peter |
// Notify others that data is ready after we have unlocked |
4043 |
01 Mar 21 |
peter |
185 |
pop_condition_.notify_one(); |
3717 |
20 Nov 17 |
peter |
186 |
} |
3717 |
20 Nov 17 |
peter |
187 |
|
3717 |
20 Nov 17 |
peter |
188 |
|
3717 |
20 Nov 17 |
peter |
189 |
/** |
3717 |
20 Nov 17 |
peter |
\return Number of elements stored in container |
3717 |
20 Nov 17 |
peter |
191 |
*/ |
3717 |
20 Nov 17 |
peter |
192 |
size_type size(void) const |
3717 |
20 Nov 17 |
peter |
193 |
{ |
3946 |
20 Jul 20 |
peter |
194 |
std::unique_lock<std::mutex> lock(mutex_); |
3717 |
20 Nov 17 |
peter |
195 |
return q_.size(); |
3717 |
20 Nov 17 |
peter |
196 |
} // lock is released here |
3717 |
20 Nov 17 |
peter |
197 |
|
3717 |
20 Nov 17 |
peter |
198 |
|
3717 |
20 Nov 17 |
peter |
199 |
/** |
3717 |
20 Nov 17 |
peter |
If Queue is empty() do nothing and return \c false, else pop |
3717 |
20 Nov 17 |
peter |
the element into \a value and return \c true |
3717 |
20 Nov 17 |
peter |
202 |
*/ |
3717 |
20 Nov 17 |
peter |
203 |
bool try_pop(T& value) |
3717 |
20 Nov 17 |
peter |
204 |
{ |
3946 |
20 Jul 20 |
peter |
205 |
std::unique_lock<std::mutex> lock(mutex_); |
3717 |
20 Nov 17 |
peter |
206 |
if (q_.empty()) |
3717 |
20 Nov 17 |
peter |
207 |
return false; |
3717 |
20 Nov 17 |
peter |
208 |
static_cast<Derived*>(this)->pop_impl(value, lock); |
4043 |
01 Mar 21 |
peter |
209 |
lock.unlock(); // unlock the mutex |
4043 |
01 Mar 21 |
peter |
210 |
|
4043 |
01 Mar 21 |
peter |
// Notify others that data is ready after we have unlocked |
4043 |
01 Mar 21 |
peter |
212 |
push_condition_.notify_one(); |
3717 |
20 Nov 17 |
peter |
213 |
return true; |
3717 |
20 Nov 17 |
peter |
214 |
} // lock is released here |
3717 |
20 Nov 17 |
peter |
215 |
|
4043 |
01 Mar 21 |
peter |
216 |
|
4043 |
01 Mar 21 |
peter |
217 |
/** |
4043 |
01 Mar 21 |
peter |
If Queue size is less than capacity push \a value and return \c |
4043 |
01 Mar 21 |
peter |
true; otherwise return \c false |
4043 |
01 Mar 21 |
peter |
220 |
|
4043 |
01 Mar 21 |
peter |
\since New in yat 0.19 |
4043 |
01 Mar 21 |
peter |
222 |
*/ |
4043 |
01 Mar 21 |
peter |
223 |
bool try_push(T& value) |
4043 |
01 Mar 21 |
peter |
224 |
{ |
4043 |
01 Mar 21 |
peter |
225 |
std::unique_lock<std::mutex> lock(mutex_); |
4043 |
01 Mar 21 |
peter |
226 |
if (q_.size() >= capacity_) |
4043 |
01 Mar 21 |
peter |
227 |
return false; |
4043 |
01 Mar 21 |
peter |
228 |
static_cast<Derived*>(this)->push_impl(value, lock); |
4043 |
01 Mar 21 |
peter |
229 |
lock.unlock(); // unlock the mutex |
4043 |
01 Mar 21 |
peter |
230 |
|
4043 |
01 Mar 21 |
peter |
// Notify others that data is ready after we have unlocked |
4043 |
01 Mar 21 |
peter |
232 |
pop_condition_.notify_one(); |
4043 |
01 Mar 21 |
peter |
233 |
return true; |
4043 |
01 Mar 21 |
peter |
234 |
} // lock is released here |
4043 |
01 Mar 21 |
peter |
235 |
|
4043 |
01 Mar 21 |
peter |
236 |
|
4043 |
01 Mar 21 |
peter |
237 |
/** |
4043 |
01 Mar 21 |
peter |
If Queue size is less than capacity push \a value and return \c |
4043 |
01 Mar 21 |
peter |
true; otherwise return \c false |
4043 |
01 Mar 21 |
peter |
240 |
|
4043 |
01 Mar 21 |
peter |
\since New in yat 0.19 |
4043 |
01 Mar 21 |
peter |
242 |
*/ |
4043 |
01 Mar 21 |
peter |
243 |
bool try_push(T&& value) |
4043 |
01 Mar 21 |
peter |
244 |
{ |
4043 |
01 Mar 21 |
peter |
245 |
std::unique_lock<std::mutex> lock(mutex_); |
4043 |
01 Mar 21 |
peter |
246 |
if (q_.size() >= capacity_) |
4043 |
01 Mar 21 |
peter |
247 |
return false; |
4043 |
01 Mar 21 |
peter |
248 |
static_cast<Derived*>(this)->push_impl(std::move(value), lock); |
4043 |
01 Mar 21 |
peter |
249 |
lock.unlock(); // unlock the mutex |
4043 |
01 Mar 21 |
peter |
250 |
|
4043 |
01 Mar 21 |
peter |
// Notify others that data is ready after we have unlocked |
4043 |
01 Mar 21 |
peter |
252 |
pop_condition_.notify_one(); |
4043 |
01 Mar 21 |
peter |
253 |
return true; |
4043 |
01 Mar 21 |
peter |
254 |
} // lock is released here |
4043 |
01 Mar 21 |
peter |
255 |
|
3717 |
20 Nov 17 |
peter |
256 |
protected: |
3717 |
20 Nov 17 |
peter |
257 |
/** |
3717 |
20 Nov 17 |
peter |
assign other to this |
3717 |
20 Nov 17 |
peter |
259 |
*/ |
3717 |
20 Nov 17 |
peter |
260 |
void assign(const BasicQueue& other) |
3717 |
20 Nov 17 |
peter |
261 |
{ |
3719 |
21 Nov 17 |
peter |
262 |
if (this != &other) { |
3946 |
20 Jul 20 |
peter |
// std::lock guarantees that the two mutexes are locked in |
3721 |
21 Nov 17 |
peter |
// the same order regardless of passed order and thereby |
3721 |
21 Nov 17 |
peter |
// avoiding deadlock when two threads are calling |
3721 |
21 Nov 17 |
peter |
// lhs.assign(rhs) and rhs.assign(lhs) simultaneously. |
3946 |
20 Jul 20 |
peter |
267 |
std::lock(mutex_, other.mutex_); |
3946 |
20 Jul 20 |
peter |
268 |
std::unique_lock<std::mutex> lock(mutex_, std::adopt_lock_t()); |
3946 |
20 Jul 20 |
peter |
269 |
std::unique_lock<std::mutex> other_lock(other.mutex_, |
3946 |
20 Jul 20 |
peter |
270 |
std::adopt_lock_t()); |
3719 |
21 Nov 17 |
peter |
271 |
q_ = other.q_; |
4043 |
01 Mar 21 |
peter |
272 |
capacity_ = other.capacity_; |
3719 |
21 Nov 17 |
peter |
273 |
} |
3717 |
20 Nov 17 |
peter |
274 |
} |
3717 |
20 Nov 17 |
peter |
275 |
|
3717 |
20 Nov 17 |
peter |
/// data |
3717 |
20 Nov 17 |
peter |
277 |
Container q_; |
3717 |
20 Nov 17 |
peter |
278 |
private: |
3946 |
20 Jul 20 |
peter |
279 |
mutable std::mutex mutex_; |
4043 |
01 Mar 21 |
peter |
280 |
std::condition_variable pop_condition_; |
4043 |
01 Mar 21 |
peter |
281 |
std::condition_variable push_condition_; |
4043 |
01 Mar 21 |
peter |
282 |
size_t capacity_; |
3717 |
20 Nov 17 |
peter |
283 |
}; |
3717 |
20 Nov 17 |
peter |
284 |
|
3717 |
20 Nov 17 |
peter |
285 |
}}}} |
3717 |
20 Nov 17 |
peter |
286 |
#endif |