libDwm-0.9.45
DwmConcurrentQueue.hh
Go to the documentation of this file.
1//===========================================================================
2// @(#) $DwmPath$
3//===========================================================================
4// Copyright (c) Daniel W. McRobb 2016
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions
9// are met:
10//
11// 1. Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// 2. Redistributions in binary form must reproduce the above copyright
14// notice, this list of conditions and the following disclaimer in the
15// documentation and/or other materials provided with the distribution.
16// 3. The names of the authors and copyright holders may not be used to
17// endorse or promote products derived from this software without
18// specific prior written permission.
19//
20// IN NO EVENT SHALL DANIEL W. MCROBB BE LIABLE TO ANY PARTY FOR
21// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
22// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE,
23// EVEN IF DANIEL W. MCROBB HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
24// DAMAGE.
25//
26// THE SOFTWARE PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND
27// DANIEL W. MCROBB HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
28// UPDATES, ENHANCEMENTS, OR MODIFICATIONS. DANIEL W. MCROBB MAKES NO
29// REPRESENTATIONS AND EXTENDS NO WARRANTIES OF ANY KIND, EITHER
30// IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31// WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE,
32// OR THAT THE USE OF THIS SOFTWARE WILL NOT INFRINGE ANY PATENT,
33// TRADEMARK OR OTHER RIGHTS.
34//===========================================================================
35
36//---------------------------------------------------------------------------
39//---------------------------------------------------------------------------
40
41#ifndef _DWMCONCURRENTQUEUE_HH_
42#define _DWMCONCURRENTQUEUE_HH_
43
44extern "C" {
45 #include <unistd.h>
46}
47
48#include <atomic>
49#include <memory>
50#include <thread>
51#include <vector>
52
53namespace Dwm {
54
55 namespace Thread {
56
57 //------------------------------------------------------------------------
89 //------------------------------------------------------------------------
90 template <typename T>
92 {
93 public:
94 //----------------------------------------------------------------------
99 //----------------------------------------------------------------------
100 ConcurrentQueue(uint64_t maxLength = 10000,
101 useconds_t pushSleepUsecs = 1000)
102 : _length(0), _maxLength(maxLength),
103 _pushSleepUsecs(pushSleepUsecs)
104 {
105 _front = _back = new Entry(T());
106 _pushingLocked = _poppingLocked = false;
107 }
108
109 //----------------------------------------------------------------------
111 //----------------------------------------------------------------------
113 {
114 while (_front != nullptr) {
115 Entry *tmp = _front;
116 _front = tmp->next;
117 delete tmp;
118 }
119 }
120
121 //----------------------------------------------------------------------
125 //----------------------------------------------------------------------
126 bool PopFront(T & result)
127 {
128 while (_poppingLocked.exchange(true));
129 if (_front->next != nullptr) {
130 // queue is not empty.
131 Entry *oldFirst = _front;
132 _front = _front->next;
133 result = _front->value;
134 _poppingLocked = false;
135 // delete the old front Entry.
136 delete oldFirst;
137 // decrement the queue length.
138 --_length;
139 return true;
140 }
141 _poppingLocked = false;
142 return false;
143 }
144
145 //----------------------------------------------------------------------
147 //----------------------------------------------------------------------
148 bool PopFront(std::vector<T> & result)
149 {
150 typename std::vector<T>::size_type numEntries = 0;
151 // lcok against others popping from the queue
152 while (_poppingLocked.exchange(true));
153 if (_front->next != nullptr) {
154 // queue is not empty. Save the existing front.
155 Entry *oldFront = _front;
156 // Move front to the end of the queue since we're going to take
157 // all entries from the queue. Keep track of how many entries
158 // we'll take, and adjust _length at end.
159 while (_front->next != nullptr) {
160 _front = _front->next;
161 ++numEntries;
162 }
163 _length -= numEntries;
164 // Save the new location of _front. This is where we'll stop
165 // popping entries. Once we unlock popping, another thread
166 // could come in and move _front.
167 Entry *newFront = _front;
168 // Unlock popping so other threads can pop entries.
169 _poppingLocked = false;
170 // Resize the output vector to hold the popped entries.
171 result.resize(numEntries);
172 // Populate the output vector and delete the old entries.
173 typename std::vector<T>::size_type i = 0;
174 Entry *entry = oldFront->next;
175 while (entry != newFront) {
176 Entry *delEntry = entry;
177 result[i] = entry->value;
178 entry = entry->next;
179 ++i;
180 delete delEntry;
181 }
182 // Delete the old front entry.
183 delete oldFront;
184 return true;
185 }
186 _poppingLocked = false;
187 return false;
188 }
189
190 //----------------------------------------------------------------------
193 //----------------------------------------------------------------------
194 bool PushBack(const T & t)
195 {
196 while (_length > _maxLength) { usleep(_pushSleepUsecs); };
197 Entry *tmp = new Entry(t);
198 while (_pushingLocked.exchange(true));
199 _back->next = tmp;
200 _back = tmp;
201 _pushingLocked = false;
202 ++_length;
203 return true;
204 }
205
206 //----------------------------------------------------------------------
208 //----------------------------------------------------------------------
209 bool PushBack(const std::vector<T> & t)
210 {
211 while (_length > _maxLength) { usleep(_pushSleepUsecs); }
212 std::vector<Entry *> newEntries(t.size());
213 typename std::vector<T>::size_type i = 0;
214 newEntries[i] = new Entry(t[i]);
215 ++i;
216 for (; i < t.size(); ++i) {
217 newEntries[i] = new Entry(t[i]);
218 newEntries[i-1]->next = newEntries[i];
219 }
220 while (_pushingLocked.exchange(true));
221 _back->next = newEntries[0];
222 _back = newEntries[t.size() - 1];
223 _pushingLocked = false;
224 _length += t.size();
225 return true;
226 }
227
228 private:
229 //----------------------------------------------------------------------
231 //----------------------------------------------------------------------
232 class Entry
233 {
234 public:
235 Entry(const T & val)
236 : value(val), next(nullptr)
237 {}
238 T value;
239 std::atomic<Entry *> next;
240 };
241
242 Entry *_front;
243 std::atomic<bool> _poppingLocked;
244 Entry *_back;
245 std::atomic<bool> _pushingLocked;
246 std::atomic<uint64_t> _length;
247 uint64_t _maxLength;
248 useconds_t _pushSleepUsecs;
249 };
250
251
252 } // namespace Thread
253
254} // namespace Dwm
255
256#endif // _DWMCONCURRENTQUEUE_HH_
257
258//---------------------------- emacs settings -----------------------------
259// Local Variables:
260// mode: C++
261// tab-width: 2
262// indent-tabs-mode: nil
263// c-basic-offset: 2
264// End:
265//-------------------------------------------------------------------------
Concurrent queue template.
Definition DwmConcurrentQueue.hh:92
~ConcurrentQueue()
Destructor. Walks the queue and deletes all entries.
Definition DwmConcurrentQueue.hh:112
bool PopFront(T &result)
If the queue is not empty, pops the front entry into result and returns true.
Definition DwmConcurrentQueue.hh:126
ConcurrentQueue(uint64_t maxLength=10000, useconds_t pushSleepUsecs=1000)
Constructor.
Definition DwmConcurrentQueue.hh:100
bool PushBack(const T &t)
Waits for the queue to not be full (see the Queue constructor), then pushes t onto the back of the qu...
Definition DwmConcurrentQueue.hh:194