diff --git a/examples/Threading/Demo_Source_Sink_Counter/Consumer.inot b/examples/Threading/Demo_Source_Sink_Counter/Consumer.inot index b5c1fd6..b03acdb 100644 --- a/examples/Threading/Demo_Source_Sink_Counter/Consumer.inot +++ b/examples/Threading/Demo_Source_Sink_Counter/Consumer.inot @@ -1,4 +1,4 @@ -SINK(counter, int); +SINK(counter, int, 10); void setup() { diff --git a/src/Arduino_Threads.h b/src/Arduino_Threads.h index 685b09c..fe05cb5 100644 --- a/src/Arduino_Threads.h +++ b/src/Arduino_Threads.h @@ -44,19 +44,37 @@ public: \ Source name; \ private: -#define SINK(name, type) \ +/* We need to call the SinkBlocking(size_t const size) + * non-default constructor using size as parameter. + + * This is achieved via + * SinkBlocking name{size}; + * instead of + * SinkBlocking name(size); + * otherwise the compiler will read it as a declaration + * of a method called "name" and we get a syntax error. + * + * This is called "C++11 uniform init" (using "{}" instead + * of "()" without "="... yikes!) + * https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++-dos-and-donts.md + */ + +#define SINK_2_ARG(name, type) \ +public: \ + SinkBlocking name{1}; \ +private: + +#define SINK_3_ARG(name, type, size) \ public: \ - SinkBlocking name; \ + SinkBlocking name{size}; \ private: -// we need to call the Sink(int size) non-default constructor using size as parameter. -// This is done by writing -// Sink name{size}; -// instead of: -// Sink name(size); -// otherwise the compiler will read it as a declaration of a method called "name" and we -// get a syntax error. -// This is called "C++11 uniform init" (using "{}" instead of "()" without "="... yikes!) -// https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++-dos-and-donts.md + +/* Black C macro magic enabling "macro overloading" + * with same name macro, but multiple arguments. + * https://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments + */ +#define GET_SINK_MACRO(_1,_2,_3,NAME,...) NAME +#define SINK(...) GET_SINK_MACRO(__VA_ARGS__, SINK_3_ARG, SINK_2_ARG)(__VA_ARGS__) #define SHARED(name, type) \ Shared name; diff --git a/src/threading/CircularBuffer.hpp b/src/threading/CircularBuffer.hpp new file mode 100644 index 0000000..b003f77 --- /dev/null +++ b/src/threading/CircularBuffer.hpp @@ -0,0 +1,118 @@ +/* + * This file is part of the Arduino_ThreadsafeIO library. + * Copyright (c) 2021 Arduino SA. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ARDUINO_THREADS_RINGBUFFER_HPP_ +#define ARDUINO_THREADS_RINGBUFFER_HPP_ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include + +/************************************************************************************** + * CLASS DECLARATION + **************************************************************************************/ + +template +class CircularBuffer +{ +public: + + CircularBuffer(size_t const size); + + void store(T const data); + T read(); + bool isFull() const; + bool isEmpty() const; + + +private: + + mbed::SharedPtr _data; + size_t const _size; + size_t _head, _tail, _num_elems; + + size_t next(size_t const idx); +}; + +/************************************************************************************** + * CTOR/DTOR + **************************************************************************************/ + +template +CircularBuffer::CircularBuffer(size_t const size) +: _data{new T[size]} +, _size{size} +, _head{0} +, _tail{0} +, _num_elems{0} +{ +} + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS + **************************************************************************************/ + +template +void CircularBuffer::store(T const data) +{ + if (!isFull()) + { + _data.get()[_head] = data; + _head = next(_head); + _num_elems++; + } +} + +template +T CircularBuffer::read() +{ + if (isEmpty()) + return T{0}; + + T const value = _data.get()[_tail]; + _tail = next(_tail); + _num_elems--; + + return value; +} + +template +bool CircularBuffer::isFull() const +{ + return (_num_elems == _size); +} + +template +bool CircularBuffer::isEmpty() const +{ + return (_num_elems == 0); +} + +/************************************************************************************** + * PRIVATE MEMBER FUNCTIONS + **************************************************************************************/ + +template +size_t CircularBuffer::next(size_t const idx) +{ + return ((idx + 1) % _size); +} + +#endif /* ARDUINO_THREADS_RINGBUFFER_HPP_ */ diff --git a/src/threading/Sink.hpp b/src/threading/Sink.hpp index 0d9c291..539ecb2 100644 --- a/src/threading/Sink.hpp +++ b/src/threading/Sink.hpp @@ -25,6 +25,8 @@ #include +#include "CircularBuffer.hpp" + /************************************************************************************** * CLASS DECLARATION **************************************************************************************/ @@ -64,7 +66,7 @@ class SinkBlocking : public SinkBase { public: - SinkBlocking(); + SinkBlocking(size_t const size); virtual ~SinkBlocking() { } virtual operator T() override; @@ -73,8 +75,7 @@ class SinkBlocking : public SinkBase private: - T _data; - bool _is_data_available; + CircularBuffer _data; rtos::Mutex _mutex; rtos::ConditionVariable _cond_data_available; rtos::ConditionVariable _cond_slot_available; @@ -106,8 +107,8 @@ void SinkNonBlocking::inject(T const & value) **************************************************************************************/ template -SinkBlocking::SinkBlocking() -: _is_data_available{false} +SinkBlocking::SinkBlocking(size_t const size) +: _data(size) , _cond_data_available(_mutex) , _cond_slot_available(_mutex) { } @@ -116,10 +117,9 @@ template SinkBlocking::operator T() { _mutex.lock(); - while (!_is_data_available) + while (_data.isEmpty()) _cond_data_available.wait(); - T const d = _data; - _is_data_available = false; + T const d = _data.read(); _cond_slot_available.notify_all(); _mutex.unlock(); return d; @@ -129,10 +129,9 @@ template void SinkBlocking::inject(T const & value) { _mutex.lock(); - while (_is_data_available) + while (_data.isFull()) _cond_slot_available.wait(); - _data = value; - _is_data_available = true; + _data.store(value); _cond_data_available.notify_all(); _mutex.unlock(); }