Soldered 125kHz RFID board Arduino library 1.0.0
Library for Soldered 125kHz RFID board.
Loading...
Searching...
No Matches
circular_queue.h
Go to the documentation of this file.
1/*
2circular_queue.h - Implementation of a lock-free circular queue for EspSoftwareSerial.
3Copyright (c) 2019 Dirk O. Kaar. All rights reserved.
4
5This library is free software; you can redistribute it and/or
6modify it under the terms of the GNU Lesser General Public
7License as published by the Free Software Foundation; either
8version 2.1 of the License, or (at your option) any later version.
9
10This library is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13Lesser General Public License for more details.
14
15You should have received a copy of the GNU Lesser General Public
16License along with this library; if not, write to the Free Software
17Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
20#ifndef __circular_queue_h
21#define __circular_queue_h
22
23#ifdef ARDUINO
24#include <Arduino.h>
25#endif
26
27#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
28#include <atomic>
29#include <memory>
30#include <algorithm>
31#include "Delegate.h"
32using std::min;
33#else
34#include "ghostl.h"
35#endif
36
37#if !defined(ESP32) && !defined(ESP8266)
38#define IRAM_ATTR
39#endif
40
46template< typename T, typename ForEachArg = void >
48{
49public:
54 {
55 m_inPos.store(0);
56 m_outPos.store(0);
57 }
62 {
63 m_inPos.store(0);
64 m_outPos.store(0);
65 }
70 {
72 }
75 {
77 m_buffer = cq.m_buffer;
78 m_inPos.store(cq.m_inPos.load());
79 m_outPos.store(cq.m_outPos.load());
80 }
82
86 size_t capacity() const
87 {
88 return m_bufSize - 1;
89 }
90
98 bool capacity(const size_t cap);
99
103 void flush()
104 {
106 }
107
111 size_t available() const
112 {
113 int avail = static_cast<int>(m_inPos.load() - m_outPos.load());
114 if (avail < 0) avail += m_bufSize;
115 return avail;
116 }
117
121 size_t available_for_push() const
122 {
123 int avail = static_cast<int>(m_outPos.load() - m_inPos.load()) - 1;
124 if (avail < 0) avail += m_bufSize;
125 return avail;
126 }
127
133 T peek() const
134 {
135 const auto outPos = m_outPos.load(std::memory_order_relaxed);
137 return m_buffer[outPos];
138 }
139
144 inline T& IRAM_ATTR pushpeek() __attribute__((always_inline))
145 {
146 const auto inPos = m_inPos.load(std::memory_order_relaxed);
148 return m_buffer[inPos];
149 }
150
156 inline bool IRAM_ATTR push() __attribute__((always_inline))
157 {
158 const auto inPos = m_inPos.load(std::memory_order_acquire);
159 const size_t next = (inPos + 1) % m_bufSize;
161 return false;
162 }
163
165
167 return true;
168 }
169
175 inline bool IRAM_ATTR push(T&& val) __attribute__((always_inline))
176 {
177 const auto inPos = m_inPos.load(std::memory_order_acquire);
178 const size_t next = (inPos + 1) % m_bufSize;
180 return false;
181 }
182
184
185 m_buffer[inPos] = std::move(val);
186
188
190 return true;
191 }
192
198 inline bool IRAM_ATTR push(const T& val) __attribute__((always_inline))
199 {
200 T v(val);
201 return push(std::move(v));
202 }
203
204#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
211 size_t push_n(const T* buffer, size_t size);
212#endif
213
219 T pop();
220
221#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
228 size_t pop_n(T* buffer, size_t size);
229#endif
230
235#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
236 void for_each(const Delegate<void(T&&), ForEachArg>& fun);
237#else
238 void for_each(Delegate<void(T&&), ForEachArg> fun);
239#endif
240
247#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
248 bool for_each_rev_requeue(const Delegate<bool(T&), ForEachArg>& fun);
249#else
250 bool for_each_rev_requeue(Delegate<bool(T&), ForEachArg> fun);
251#endif
252
253protected:
254 const T defaultValue = {};
255 size_t m_bufSize;
256#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
258#else
260#endif
263};
264
265template< typename T, typename ForEachArg >
267{
268 if (cap + 1 == m_bufSize) return true;
269 else if (available() > cap) return false;
270 std::unique_ptr<T[] > buffer(new T[cap + 1]);
271 const auto available = pop_n(buffer, cap);
272 m_buffer.reset(buffer);
273 m_bufSize = cap + 1;
275 m_inPos.store(available, std::memory_order_relaxed);
276 m_outPos.store(0, std::memory_order_release);
277 return true;
278}
279
280#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
281template< typename T, typename ForEachArg >
282size_t circular_queue<T, ForEachArg>::push_n(const T* buffer, size_t size)
283{
284 const auto inPos = m_inPos.load(std::memory_order_acquire);
285 const auto outPos = m_outPos.load(std::memory_order_relaxed);
286
287 size_t blockSize = (outPos > inPos) ? outPos - 1 - inPos : (outPos == 0) ? m_bufSize - 1 - inPos : m_bufSize - inPos;
288 blockSize = min(size, blockSize);
289 if (!blockSize) return 0;
290 int next = (inPos + blockSize) % m_bufSize;
291
293
294 auto dest = m_buffer.get() + inPos;
295 std::copy_n(std::make_move_iterator(buffer), blockSize, dest);
296 size = min(size - blockSize, outPos > 1 ? static_cast<size_t>(outPos - next - 1) : 0);
297 next += size;
298 dest = m_buffer.get();
299 std::copy_n(std::make_move_iterator(buffer + blockSize), size, dest);
300
302
303 m_inPos.store(next, std::memory_order_release);
304 return blockSize + size;
305}
306#endif
307
308template< typename T, typename ForEachArg >
310{
311 const auto outPos = m_outPos.load(std::memory_order_acquire);
312 if (m_inPos.load(std::memory_order_relaxed) == outPos) return defaultValue;
313
315
316 auto val = std::move(m_buffer[outPos]);
317
319
320 m_outPos.store((outPos + 1) % m_bufSize, std::memory_order_release);
321 return val;
322}
323
324#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
325template< typename T, typename ForEachArg >
326size_t circular_queue<T, ForEachArg>::pop_n(T* buffer, size_t size) {
327 size_t avail = size = min(size, available());
328 if (!avail) return 0;
329 const auto outPos = m_outPos.load(std::memory_order_acquire);
330 size_t n = min(avail, static_cast<size_t>(m_bufSize - outPos));
331
333
334 if (buffer) {
335 buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer);
336 avail -= n;
337 std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer);
338 }
339
341
342 m_outPos.store((outPos + size) % m_bufSize, std::memory_order_release);
343 return size;
344}
345#endif
346
347template< typename T, typename ForEachArg >
348#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
349void circular_queue<T, ForEachArg>::for_each(const Delegate<void(T&&), ForEachArg>& fun)
350#else
351void circular_queue<T, ForEachArg>::for_each(Delegate<void(T&&), ForEachArg> fun)
352#endif
353{
354 auto outPos = m_outPos.load(std::memory_order_acquire);
355 const auto inPos = m_inPos.load(std::memory_order_relaxed);
357 while (outPos != inPos)
358 {
359 fun(std::move(m_buffer[outPos]));
361 outPos = (outPos + 1) % m_bufSize;
362 m_outPos.store(outPos, std::memory_order_release);
363 }
364}
365
366template< typename T, typename ForEachArg >
367#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
368bool circular_queue<T, ForEachArg>::for_each_rev_requeue(const Delegate<bool(T&), ForEachArg>& fun)
369#else
371#endif
372{
376 if (outPos == inPos0) return false;
377 auto pos = inPos0;
378 auto outPos1 = inPos0;
379 const auto posDecr = circular_queue<T, ForEachArg>::m_bufSize - 1;
380 do {
381 pos = (pos + posDecr) % circular_queue<T, ForEachArg>::m_bufSize;
383 if (fun(val))
384 {
385 outPos1 = (outPos1 + posDecr) % circular_queue<T, ForEachArg>::m_bufSize;
386 if (outPos1 != pos) circular_queue<T, ForEachArg>::m_buffer[outPos1] = std::move(val);
387 }
388 } while (pos != outPos);
390 return true;
391}
392
393#endif // __circular_queue_h
Definition Delegate.h:2049
Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO)....
Definition circular_queue.h:48
size_t push_n(const T *buffer, size_t size)
Push copies of multiple elements from a buffer into the queue, in order, beginning at buffer's head.
Definition circular_queue.h:282
circular_queue & operator=(circular_queue &&cq)
Definition circular_queue.h:74
void for_each(const Delegate< void(T &&), ForEachArg > &fun)
Iterate over and remove each available element from queue, calling back fun with an rvalue reference ...
circular_queue & operator=(const circular_queue &)=delete
void for_each(Delegate< void(T &&), ForEachArg > fun)
~circular_queue()
Definition circular_queue.h:69
bool for_each_rev_requeue(Delegate< bool(T &), ForEachArg > fun)
T pop()
Pop the next available element from the queue.
Definition circular_queue.h:309
bool capacity(const size_t cap)
Resize the queue. The available elements in the queue are preserved. This is not lock-free and concur...
Definition circular_queue.h:266
const T defaultValue
Definition circular_queue.h:254
size_t available() const
Get a snapshot number of elements that can be retrieved by pop.
Definition circular_queue.h:111
circular_queue(const size_t capacity)
Constructs a queue of the given maximum capacity.
Definition circular_queue.h:61
bool IRAM_ATTR push(const T &val) __attribute__((always_inline))
Push a copy of the parameter into the queue.
Definition circular_queue.h:198
std::unique_ptr< T > m_buffer
Definition circular_queue.h:259
void flush()
Discard all data in the queue.
Definition circular_queue.h:103
circular_queue(circular_queue &&cq)
Definition circular_queue.h:66
std::unique_ptr< T[]> m_buffer
Definition circular_queue.h:257
size_t pop_n(T *buffer, size_t size)
Pop multiple elements in ordered sequence from the queue to a buffer. If buffer is nullptr,...
Definition circular_queue.h:326
size_t capacity() const
Get the numer of elements the queue can hold at most.
Definition circular_queue.h:86
std::atomic< size_t > m_inPos
Definition circular_queue.h:261
circular_queue()
Constructs a valid, but zero-capacity dummy queue.
Definition circular_queue.h:53
T peek() const
Peek at the next element pop will return without removing it from the queue.
Definition circular_queue.h:133
bool IRAM_ATTR push(T &&val) __attribute__((always_inline))
Move the rvalue parameter into the queue.
Definition circular_queue.h:175
size_t available_for_push() const
Get the remaining free elementes for pushing.
Definition circular_queue.h:121
size_t m_bufSize
Definition circular_queue.h:255
bool for_each_rev_requeue(const Delegate< bool(T &), ForEachArg > &fun)
In reverse order, iterate over, pop and optionally requeue each available element from the queue,...
circular_queue(const circular_queue &)=delete
T &IRAM_ATTR pushpeek() __attribute__((always_inline))
Peek at the next pending input value.
Definition circular_queue.h:144
bool IRAM_ATTR push() __attribute__((always_inline))
Release the next pending input value, accessible by pushpeek(), into the queue.
Definition circular_queue.h:156
std::atomic< size_t > m_outPos
Definition circular_queue.h:262
Definition ghostl.h:40
T load(std::memory_order=std::memory_order_seq_cst) const volatile noexcept
Definition ghostl.h:47
void store(T desired, std::memory_order=std::memory_order_seq_cst) volatile noexcept
Definition ghostl.h:46
Definition ghostl.h:62
void reset(pointer p=pointer()) noexcept
Definition ghostl.h:69
void atomic_thread_fence(std::memory_order order) noexcept
Definition ghostl.h:49
T && move(T &t) noexcept
Definition ghostl.h:50
@ memory_order_relaxed
Definition ghostl.h:35
@ memory_order_release
Definition ghostl.h:37
@ memory_order_acquire
Definition ghostl.h:36