From 776d49b2f570b93fe88ad7a082519b4fb56be817 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Mon, 16 Sep 2019 01:05:47 +0200 Subject: [PATCH] Latest circular_queue from CoopTask work - ghostl.h C++ STL substitute, building for AVR --- library.json | 2 +- library.properties | 2 +- src/circular_queue/circular_queue.h | 90 ++++++++++++++++++++++---- src/circular_queue/circular_queue_mp.h | 10 ++- src/circular_queue/ghostl.h | 67 +++++++++++++++++++ 5 files changed, 156 insertions(+), 15 deletions(-) create mode 100644 src/circular_queue/ghostl.h diff --git a/library.json b/library.json index 9d477d8..c5b0b7c 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "EspSoftwareSerial", - "version": "5.2.9", + "version": "5.3.0", "keywords": [ "serial", "io", "softwareserial" ], diff --git a/library.properties b/library.properties index 23fd053..40195e4 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=EspSoftwareSerial -version=5.2.9 +version=5.3.0 author=Peter Lerup, Dirk Kaar maintainer=Peter Lerup sentence=Implementation of the Arduino software serial for ESP8266/ESP32. diff --git a/src/circular_queue/circular_queue.h b/src/circular_queue/circular_queue.h index 7564ee8..58127a7 100644 --- a/src/circular_queue/circular_queue.h +++ b/src/circular_queue/circular_queue.h @@ -23,18 +23,22 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #ifdef ARDUINO #include #endif + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) #include #include #include #include +using std::min; +#else +#include "ghostl.h" +#endif #if !defined(ESP32) && !defined(ESP8266) #define ICACHE_RAM_ATTR #define IRAM_ATTR #endif -using std::min; - /*! @brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO). This implementation is lock-free between producer and consumer for the available(), peek(), @@ -123,19 +127,35 @@ class circular_queue } /*! - @brief Peek at the next element pop returns without removing it from the queue. - @return An rvalue copy of the next element that can be popped, or a default - value of type T if the queue is empty. + @brief Peek at the next element pop will return without removing it from the queue. + @return An rvalue copy of the next element that can be popped. If the queue is empty, + return an rvalue copy of the element that is pending the next push. */ T peek() const { - const auto outPos = m_outPos.load(std::memory_order_acquire); + const auto outPos = m_outPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + return m_buffer[outPos]; + } + + /*! + @brief Peek at the next pending input value. + @return A reference to the next element that can be pushed. + */ + T& IRAM_ATTR pushpeek() + { const auto inPos = m_inPos.load(std::memory_order_relaxed); std::atomic_thread_fence(std::memory_order_acquire); - if (inPos == outPos) return defaultValue; - else return m_buffer[outPos]; + return m_buffer[inPos]; } + /*! + @brief Release the next pending input value, accessible by pushpeek(), into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(); + /*! @brief Move the rvalue parameter into the queue. @return true if the queue accepted the value, false if the queue @@ -153,6 +173,7 @@ class circular_queue return push(T(val)); } +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) /*! @brief Push copies of multiple elements from a buffer into the queue, in order, beginning at buffer's head. @@ -160,6 +181,7 @@ class circular_queue from the buffer head. */ size_t push_n(const T* buffer, size_t size); +#endif /*! @brief Pop the next available element from the queue. @@ -168,18 +190,25 @@ class circular_queue */ T pop(); +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) /*! @brief Pop multiple elements in ordered sequence from the queue to a buffer. + If buffer is nullptr, simply discards up to size elements from the queue. @return The number of elements actually popped from the queue to buffer. */ size_t pop_n(T* buffer, size_t size); +#endif /*! @brief Iterate over and remove each available element from queue, calling back fun with an rvalue reference of every single element. */ +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) void for_each(const std::function& fun); +#else + void for_each(std::function fun); +#endif /*! @brief In reverse order, iterate over, pop and optionally requeue each available element from the queue, @@ -187,12 +216,20 @@ class circular_queue Requeuing is dependent on the return boolean of the callback function. If it returns true, the requeue occurs. */ +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) bool for_each_rev_requeue(const std::function& fun); +#else + bool for_each_rev_requeue(std::function fun); +#endif protected: const T defaultValue = {}; unsigned m_bufSize; - std::unique_ptr m_buffer; +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + std::unique_ptr m_buffer; +#else + std::unique_ptr m_buffer; +#endif std::atomic m_inPos; std::atomic m_outPos; }; @@ -212,6 +249,21 @@ bool circular_queue::capacity(const size_t cap) return true; } +template< typename T > +bool IRAM_ATTR circular_queue::push() +{ + const auto inPos = m_inPos.load(std::memory_order_acquire); + const unsigned next = (inPos + 1) % m_bufSize; + if (next == m_outPos.load(std::memory_order_relaxed)) { + return false; + } + + std::atomic_thread_fence(std::memory_order_acquire); + + m_inPos.store(next, std::memory_order_release); + return true; +} + template< typename T > bool IRAM_ATTR circular_queue::push(T&& val) { @@ -231,6 +283,7 @@ bool IRAM_ATTR circular_queue::push(T&& val) return true; } +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) template< typename T > size_t circular_queue::push_n(const T* buffer, size_t size) { @@ -256,6 +309,7 @@ size_t circular_queue::push_n(const T* buffer, size_t size) m_inPos.store(next, std::memory_order_release); return blockSize + size; } +#endif template< typename T > T circular_queue::pop() @@ -273,6 +327,7 @@ T circular_queue::pop() return val; } +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) template< typename T > size_t circular_queue::pop_n(T* buffer, size_t size) { size_t avail = size = min(size, available()); @@ -282,18 +337,25 @@ size_t circular_queue::pop_n(T* buffer, size_t size) { std::atomic_thread_fence(std::memory_order_acquire); - buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer); - avail -= n; - std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer); + if (buffer) { + buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer); + avail -= n; + std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer); + } std::atomic_thread_fence(std::memory_order_release); m_outPos.store((outPos + size) % m_bufSize, std::memory_order_release); return size; } +#endif template< typename T > +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) void circular_queue::for_each(const std::function& fun) +#else +void circular_queue::for_each(std::function fun) +#endif { auto outPos = m_outPos.load(std::memory_order_acquire); const auto inPos = m_inPos.load(std::memory_order_relaxed); @@ -308,7 +370,11 @@ void circular_queue::for_each(const std::function& fun) } template< typename T > +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) bool circular_queue::for_each_rev_requeue(const std::function& fun) +#else +bool circular_queue::for_each_rev_requeue(std::function fun) +#endif { auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); diff --git a/src/circular_queue/circular_queue_mp.h b/src/circular_queue/circular_queue_mp.h index b0d12fb..1f8d301 100644 --- a/src/circular_queue/circular_queue_mp.h +++ b/src/circular_queue/circular_queue_mp.h @@ -71,6 +71,14 @@ class circular_queue_mp : protected circular_queue return circular_queue::capacity(cap); } + bool IRAM_ATTR push() = delete; + + /*! + @brief Move the rvalue parameter into the queue, guarded + for multiple concurrent producers. + @return true if the queue accepted the value, false if the queue + was full. + */ bool IRAM_ATTR push(T&& val) { #ifdef ESP8266 @@ -82,7 +90,7 @@ class circular_queue_mp : protected circular_queue } /*! - @brief Move the rvalue parameter into the queue, guarded + @brief Push a copy of the parameter into the queue, guarded for multiple concurrent producers. @return true if the queue accepted the value, false if the queue was full. diff --git a/src/circular_queue/ghostl.h b/src/circular_queue/ghostl.h new file mode 100644 index 0000000..5f65388 --- /dev/null +++ b/src/circular_queue/ghostl.h @@ -0,0 +1,67 @@ +/* +ghostl.h - Implementation of a bare-bones, mostly no-op, C++ STL shell + that allows building some Arduino ESP8266/ESP32 + libraries on Aruduino AVR. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __ghostl_h +#define __ghostl_h + +#include + +namespace std +{ + template< typename T > class unique_ptr + { + public: + using pointer = T *; + unique_ptr() noexcept : ptr(nullptr) {} + unique_ptr(pointer p) : ptr(p) {} + pointer operator->() const noexcept { return ptr; } + T& operator[](size_t i) const { return ptr[i]; } + void reset(pointer p = pointer()) noexcept + { + delete ptr; + ptr = p; + } + T& operator*() const { return *ptr; } + private: + pointer ptr; + }; + + typedef enum memory_order { + memory_order_relaxed, + memory_order_acquire, + memory_order_release, + memory_order_seq_cst + } memory_order; + template< typename T > class atomic { + private: + T value; + public: + atomic() {} + atomic(T desired) { value = desired; } + void store(T desired, std::memory_order = std::memory_order_seq_cst) volatile noexcept { value = desired; } + T load(std::memory_order = std::memory_order_seq_cst) const volatile noexcept { return value; } + }; + inline void atomic_thread_fence(std::memory_order order) noexcept {} + template< typename T > T&& move(T& t) noexcept { return static_cast(t); } + template< typename T > using function = T *; +} + +#endif // __ghostl_h