Skip to content

Commit 776d49b

Browse files
committed
Latest circular_queue from CoopTask work - ghostl.h C++ STL substitute, building for AVR
1 parent 35d9c14 commit 776d49b

File tree

5 files changed

+156
-15
lines changed

5 files changed

+156
-15
lines changed

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "EspSoftwareSerial",
3-
"version": "5.2.9",
3+
"version": "5.3.0",
44
"keywords": [
55
"serial", "io", "softwareserial"
66
],

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=EspSoftwareSerial
2-
version=5.2.9
2+
version=5.3.0
33
author=Peter Lerup, Dirk Kaar
44
maintainer=Peter Lerup <peter@lerup.com>
55
sentence=Implementation of the Arduino software serial for ESP8266/ESP32.

src/circular_queue/circular_queue.h

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,22 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2323
#ifdef ARDUINO
2424
#include <Arduino.h>
2525
#endif
26+
27+
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
2628
#include <atomic>
2729
#include <memory>
2830
#include <algorithm>
2931
#include <functional>
32+
using std::min;
33+
#else
34+
#include "ghostl.h"
35+
#endif
3036

3137
#if !defined(ESP32) && !defined(ESP8266)
3238
#define ICACHE_RAM_ATTR
3339
#define IRAM_ATTR
3440
#endif
3541

36-
using std::min;
37-
3842
/*!
3943
@brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO).
4044
This implementation is lock-free between producer and consumer for the available(), peek(),
@@ -123,19 +127,35 @@ class circular_queue
123127
}
124128

125129
/*!
126-
@brief Peek at the next element pop returns without removing it from the queue.
127-
@return An rvalue copy of the next element that can be popped, or a default
128-
value of type T if the queue is empty.
130+
@brief Peek at the next element pop will return without removing it from the queue.
131+
@return An rvalue copy of the next element that can be popped. If the queue is empty,
132+
return an rvalue copy of the element that is pending the next push.
129133
*/
130134
T peek() const
131135
{
132-
const auto outPos = m_outPos.load(std::memory_order_acquire);
136+
const auto outPos = m_outPos.load(std::memory_order_relaxed);
137+
std::atomic_thread_fence(std::memory_order_acquire);
138+
return m_buffer[outPos];
139+
}
140+
141+
/*!
142+
@brief Peek at the next pending input value.
143+
@return A reference to the next element that can be pushed.
144+
*/
145+
T& IRAM_ATTR pushpeek()
146+
{
133147
const auto inPos = m_inPos.load(std::memory_order_relaxed);
134148
std::atomic_thread_fence(std::memory_order_acquire);
135-
if (inPos == outPos) return defaultValue;
136-
else return m_buffer[outPos];
149+
return m_buffer[inPos];
137150
}
138151

152+
/*!
153+
@brief Release the next pending input value, accessible by pushpeek(), into the queue.
154+
@return true if the queue accepted the value, false if the queue
155+
was full.
156+
*/
157+
bool IRAM_ATTR push();
158+
139159
/*!
140160
@brief Move the rvalue parameter into the queue.
141161
@return true if the queue accepted the value, false if the queue
@@ -153,13 +173,15 @@ class circular_queue
153173
return push(T(val));
154174
}
155175

176+
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
156177
/*!
157178
@brief Push copies of multiple elements from a buffer into the queue,
158179
in order, beginning at buffer's head.
159180
@return The number of elements actually copied into the queue, counted
160181
from the buffer head.
161182
*/
162183
size_t push_n(const T* buffer, size_t size);
184+
#endif
163185

164186
/*!
165187
@brief Pop the next available element from the queue.
@@ -168,31 +190,46 @@ class circular_queue
168190
*/
169191
T pop();
170192

193+
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
171194
/*!
172195
@brief Pop multiple elements in ordered sequence from the queue to a buffer.
196+
If buffer is nullptr, simply discards up to size elements from the queue.
173197
@return The number of elements actually popped from the queue to
174198
buffer.
175199
*/
176200
size_t pop_n(T* buffer, size_t size);
201+
#endif
177202

178203
/*!
179204
@brief Iterate over and remove each available element from queue,
180205
calling back fun with an rvalue reference of every single element.
181206
*/
207+
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
182208
void for_each(const std::function<void(T&&)>& fun);
209+
#else
210+
void for_each(std::function<void(T&&)> fun);
211+
#endif
183212

184213
/*!
185214
@brief In reverse order, iterate over, pop and optionally requeue each available element from the queue,
186215
calling back fun with a reference of every single element.
187216
Requeuing is dependent on the return boolean of the callback function. If it
188217
returns true, the requeue occurs.
189218
*/
219+
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
190220
bool for_each_rev_requeue(const std::function<bool(T&)>& fun);
221+
#else
222+
bool for_each_rev_requeue(std::function<bool(T&)> fun);
223+
#endif
191224

192225
protected:
193226
const T defaultValue = {};
194227
unsigned m_bufSize;
195-
std::unique_ptr<T[] > m_buffer;
228+
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
229+
std::unique_ptr<T[]> m_buffer;
230+
#else
231+
std::unique_ptr<T> m_buffer;
232+
#endif
196233
std::atomic<unsigned> m_inPos;
197234
std::atomic<unsigned> m_outPos;
198235
};
@@ -212,6 +249,21 @@ bool circular_queue<T>::capacity(const size_t cap)
212249
return true;
213250
}
214251

252+
template< typename T >
253+
bool IRAM_ATTR circular_queue<T>::push()
254+
{
255+
const auto inPos = m_inPos.load(std::memory_order_acquire);
256+
const unsigned next = (inPos + 1) % m_bufSize;
257+
if (next == m_outPos.load(std::memory_order_relaxed)) {
258+
return false;
259+
}
260+
261+
std::atomic_thread_fence(std::memory_order_acquire);
262+
263+
m_inPos.store(next, std::memory_order_release);
264+
return true;
265+
}
266+
215267
template< typename T >
216268
bool IRAM_ATTR circular_queue<T>::push(T&& val)
217269
{
@@ -231,6 +283,7 @@ bool IRAM_ATTR circular_queue<T>::push(T&& val)
231283
return true;
232284
}
233285

286+
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
234287
template< typename T >
235288
size_t circular_queue<T>::push_n(const T* buffer, size_t size)
236289
{
@@ -256,6 +309,7 @@ size_t circular_queue<T>::push_n(const T* buffer, size_t size)
256309
m_inPos.store(next, std::memory_order_release);
257310
return blockSize + size;
258311
}
312+
#endif
259313

260314
template< typename T >
261315
T circular_queue<T>::pop()
@@ -273,6 +327,7 @@ T circular_queue<T>::pop()
273327
return val;
274328
}
275329

330+
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
276331
template< typename T >
277332
size_t circular_queue<T>::pop_n(T* buffer, size_t size) {
278333
size_t avail = size = min(size, available());
@@ -282,18 +337,25 @@ size_t circular_queue<T>::pop_n(T* buffer, size_t size) {
282337

283338
std::atomic_thread_fence(std::memory_order_acquire);
284339

285-
buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer);
286-
avail -= n;
287-
std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer);
340+
if (buffer) {
341+
buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer);
342+
avail -= n;
343+
std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer);
344+
}
288345

289346
std::atomic_thread_fence(std::memory_order_release);
290347

291348
m_outPos.store((outPos + size) % m_bufSize, std::memory_order_release);
292349
return size;
293350
}
351+
#endif
294352

295353
template< typename T >
354+
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
296355
void circular_queue<T>::for_each(const std::function<void(T&&)>& fun)
356+
#else
357+
void circular_queue<T>::for_each(std::function<void(T&&)> fun)
358+
#endif
297359
{
298360
auto outPos = m_outPos.load(std::memory_order_acquire);
299361
const auto inPos = m_inPos.load(std::memory_order_relaxed);
@@ -308,7 +370,11 @@ void circular_queue<T>::for_each(const std::function<void(T&&)>& fun)
308370
}
309371

310372
template< typename T >
373+
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
311374
bool circular_queue<T>::for_each_rev_requeue(const std::function<bool(T&)>& fun)
375+
#else
376+
bool circular_queue<T>::for_each_rev_requeue(std::function<bool(T&)> fun)
377+
#endif
312378
{
313379
auto inPos0 = circular_queue<T>::m_inPos.load(std::memory_order_acquire);
314380
auto outPos = circular_queue<T>::m_outPos.load(std::memory_order_relaxed);

src/circular_queue/circular_queue_mp.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ class circular_queue_mp : protected circular_queue<T>
7171
return circular_queue<T>::capacity(cap);
7272
}
7373

74+
bool IRAM_ATTR push() = delete;
75+
76+
/*!
77+
@brief Move the rvalue parameter into the queue, guarded
78+
for multiple concurrent producers.
79+
@return true if the queue accepted the value, false if the queue
80+
was full.
81+
*/
7482
bool IRAM_ATTR push(T&& val)
7583
{
7684
#ifdef ESP8266
@@ -82,7 +90,7 @@ class circular_queue_mp : protected circular_queue<T>
8290
}
8391

8492
/*!
85-
@brief Move the rvalue parameter into the queue, guarded
93+
@brief Push a copy of the parameter into the queue, guarded
8694
for multiple concurrent producers.
8795
@return true if the queue accepted the value, false if the queue
8896
was full.

src/circular_queue/ghostl.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
ghostl.h - Implementation of a bare-bones, mostly no-op, C++ STL shell
3+
that allows building some Arduino ESP8266/ESP32
4+
libraries on Aruduino AVR.
5+
Copyright (c) 2019 Dirk O. Kaar. All rights reserved.
6+
7+
This library is free software; you can redistribute it and/or
8+
modify it under the terms of the GNU Lesser General Public
9+
License as published by the Free Software Foundation; either
10+
version 2.1 of the License, or (at your option) any later version.
11+
12+
This library is distributed in the hope that it will be useful,
13+
but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
Lesser General Public License for more details.
16+
17+
You should have received a copy of the GNU Lesser General Public
18+
License along with this library; if not, write to the Free Software
19+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20+
*/
21+
22+
#ifndef __ghostl_h
23+
#define __ghostl_h
24+
25+
#include <util/atomic.h>
26+
27+
namespace std
28+
{
29+
template< typename T > class unique_ptr
30+
{
31+
public:
32+
using pointer = T *;
33+
unique_ptr() noexcept : ptr(nullptr) {}
34+
unique_ptr(pointer p) : ptr(p) {}
35+
pointer operator->() const noexcept { return ptr; }
36+
T& operator[](size_t i) const { return ptr[i]; }
37+
void reset(pointer p = pointer()) noexcept
38+
{
39+
delete ptr;
40+
ptr = p;
41+
}
42+
T& operator*() const { return *ptr; }
43+
private:
44+
pointer ptr;
45+
};
46+
47+
typedef enum memory_order {
48+
memory_order_relaxed,
49+
memory_order_acquire,
50+
memory_order_release,
51+
memory_order_seq_cst
52+
} memory_order;
53+
template< typename T > class atomic {
54+
private:
55+
T value;
56+
public:
57+
atomic() {}
58+
atomic(T desired) { value = desired; }
59+
void store(T desired, std::memory_order = std::memory_order_seq_cst) volatile noexcept { value = desired; }
60+
T load(std::memory_order = std::memory_order_seq_cst) const volatile noexcept { return value; }
61+
};
62+
inline void atomic_thread_fence(std::memory_order order) noexcept {}
63+
template< typename T > T&& move(T& t) noexcept { return static_cast<T&&>(t); }
64+
template< typename T > using function = T *;
65+
}
66+
67+
#endif // __ghostl_h

0 commit comments

Comments
 (0)