From 12caec4dd5ba371af4a7efb47d82816d5a7080f5 Mon Sep 17 00:00:00 2001 From: Alexander Entinger <cto@lxrobotics.com> Date: Thu, 9 Jun 2022 06:27:59 +0200 Subject: [PATCH 1/8] Shared: providing set/get method as alternative for operator overloading. --- src/threading/Shared.hpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/threading/Shared.hpp b/src/threading/Shared.hpp index 8b35cc9..6ed1b13 100644 --- a/src/threading/Shared.hpp +++ b/src/threading/Shared.hpp @@ -40,6 +40,9 @@ class Shared { public: + T get(); + void set(T const & val); + operator T(); void operator = (T const & other); inline T peek() const { return _val; } @@ -57,7 +60,7 @@ class Shared **************************************************************************************/ template<class T, size_t QUEUE_SIZE> -Shared<T,QUEUE_SIZE>::operator T() +T Shared<T,QUEUE_SIZE>::get() { T * val_ptr = _mailbox.try_get_for(rtos::Kernel::wait_for_u32_forever); if (val_ptr) @@ -70,7 +73,7 @@ Shared<T,QUEUE_SIZE>::operator T() } template<class T, size_t QUEUE_SIZE> -void Shared<T,QUEUE_SIZE>::operator = (T const & other) +void Shared<T,QUEUE_SIZE>::set(T const & val) { /* If the mailbox is full we are discarding the * oldest element and then push the new one into @@ -92,4 +95,16 @@ void Shared<T,QUEUE_SIZE>::operator = (T const & other) } } +template<class T, size_t QUEUE_SIZE> +Shared<T,QUEUE_SIZE>::operator T() +{ + return get(); +} + +template<class T, size_t QUEUE_SIZE> +void Shared<T,QUEUE_SIZE>::operator = (T const & other) +{ + set(other); +} + #endif /* ARDUINO_THREADS_SHARED_HPP_ */ From fe399afc41398c1f5773df37ed98898e61884fe8 Mon Sep 17 00:00:00 2001 From: Alexander Entinger <cto@lxrobotics.com> Date: Thu, 9 Jun 2022 06:35:51 +0200 Subject: [PATCH 2/8] Source: Providing set() method as alternate for operator overloading. --- src/threading/Source.hpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/threading/Source.hpp b/src/threading/Source.hpp index d75ac8c..ebe531a 100644 --- a/src/threading/Source.hpp +++ b/src/threading/Source.hpp @@ -43,7 +43,8 @@ class Source public: void connectTo(SinkBase<T> & sink); - void operator = (T const & other); + void set(T const & val); + void operator = (T const & val); private: std::list<SinkBase<T> *> _sink_list; @@ -60,14 +61,20 @@ void Source<T>::connectTo(SinkBase<T> & sink) } template<typename T> -void Source<T>::operator = (T const & value) +void Source<T>::set(T const & val) { std::for_each(std::begin(_sink_list), std::end (_sink_list), - [value](SinkBase<T> * sink) + [val](SinkBase<T> * sink) { - sink->inject(value); + sink->inject(val); }); } +template<typename T> +void Source<T>::operator = (T const & val) +{ + set(val); +} + #endif /* ARDUINO_THREADS_SOURCE_HPP_ */ From 0b90807a799d0ac26b8653e128a645f0fb55acfa Mon Sep 17 00:00:00 2001 From: Alexander Entinger <cto@lxrobotics.com> Date: Thu, 9 Jun 2022 06:40:00 +0200 Subject: [PATCH 3/8] Sink: Providing get() method as alternate for operator overloading. --- src/threading/Sink.hpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/threading/Sink.hpp b/src/threading/Sink.hpp index 539ecb2..23a6db9 100644 --- a/src/threading/Sink.hpp +++ b/src/threading/Sink.hpp @@ -38,8 +38,10 @@ class SinkBase virtual ~SinkBase() { } - virtual operator T() = 0; + virtual T get() = 0; virtual void inject(T const & value) = 0; + + inline operator T() { return get(); } }; template<typename T> @@ -50,7 +52,7 @@ class SinkNonBlocking : public SinkBase<T> SinkNonBlocking() { } virtual ~SinkNonBlocking() { } - virtual operator T() override; + virtual T get() override; virtual void inject(T const & value) override; @@ -69,7 +71,7 @@ class SinkBlocking : public SinkBase<T> SinkBlocking(size_t const size); virtual ~SinkBlocking() { } - virtual operator T() override; + virtual T get() override; virtual void inject(T const & value) override; @@ -87,7 +89,7 @@ class SinkBlocking : public SinkBase<T> **************************************************************************************/ template<typename T> -SinkNonBlocking<T>::operator T() +T SinkNonBlocking<T>::get() { _mutex.lock(); return _data; @@ -114,7 +116,7 @@ SinkBlocking<T>::SinkBlocking(size_t const size) { } template<typename T> -SinkBlocking<T>::operator T() +T SinkBlocking<T>::get() { _mutex.lock(); while (_data.isEmpty()) From 45db5daccb5075d75d258bc2a0b8f597295a1f35 Mon Sep 17 00:00:00 2001 From: Alexander Entinger <cto@lxrobotics.com> Date: Thu, 9 Jun 2022 06:54:57 +0200 Subject: [PATCH 4/8] Fixing compilation error. --- src/threading/Shared.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/threading/Shared.hpp b/src/threading/Shared.hpp index 6ed1b13..1708340 100644 --- a/src/threading/Shared.hpp +++ b/src/threading/Shared.hpp @@ -44,7 +44,7 @@ class Shared void set(T const & val); operator T(); - void operator = (T const & other); + void operator = (T const & val); inline T peek() const { return _val; } @@ -85,12 +85,12 @@ void Shared<T,QUEUE_SIZE>::set(T const & val) _mailbox.free(val_ptr); } - _val = other; + _val = val; T * val_ptr = _mailbox.try_alloc(); if (val_ptr) { - *val_ptr = other; + *val_ptr = val; _mailbox.put(val_ptr); } } @@ -102,9 +102,9 @@ Shared<T,QUEUE_SIZE>::operator T() } template<class T, size_t QUEUE_SIZE> -void Shared<T,QUEUE_SIZE>::operator = (T const & other) +void Shared<T,QUEUE_SIZE>::operator = (T const & val) { - set(other); + set(val); } #endif /* ARDUINO_THREADS_SHARED_HPP_ */ From 43a4d7e4e276c11ef659bf0b446e25f1785dccab Mon Sep 17 00:00:00 2001 From: Alexander Entinger <cto@lxrobotics.com> Date: Mon, 20 Jun 2022 10:14:54 +0200 Subject: [PATCH 5/8] Rename set/get to push/pop to better reflect on the fact that its not a simple variable but a queue. --- src/threading/Shared.hpp | 12 ++++++------ src/threading/Sink.hpp | 12 ++++++------ src/threading/Source.hpp | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/threading/Shared.hpp b/src/threading/Shared.hpp index 1708340..9b7835c 100644 --- a/src/threading/Shared.hpp +++ b/src/threading/Shared.hpp @@ -40,8 +40,8 @@ class Shared { public: - T get(); - void set(T const & val); + T pop(); + void push(T const & val); operator T(); void operator = (T const & val); @@ -60,7 +60,7 @@ class Shared **************************************************************************************/ template<class T, size_t QUEUE_SIZE> -T Shared<T,QUEUE_SIZE>::get() +T Shared<T,QUEUE_SIZE>::pop() { T * val_ptr = _mailbox.try_get_for(rtos::Kernel::wait_for_u32_forever); if (val_ptr) @@ -73,7 +73,7 @@ T Shared<T,QUEUE_SIZE>::get() } template<class T, size_t QUEUE_SIZE> -void Shared<T,QUEUE_SIZE>::set(T const & val) +void Shared<T,QUEUE_SIZE>::push(T const & val) { /* If the mailbox is full we are discarding the * oldest element and then push the new one into @@ -98,13 +98,13 @@ void Shared<T,QUEUE_SIZE>::set(T const & val) template<class T, size_t QUEUE_SIZE> Shared<T,QUEUE_SIZE>::operator T() { - return get(); + return pop(); } template<class T, size_t QUEUE_SIZE> void Shared<T,QUEUE_SIZE>::operator = (T const & val) { - set(val); + push(val); } #endif /* ARDUINO_THREADS_SHARED_HPP_ */ diff --git a/src/threading/Sink.hpp b/src/threading/Sink.hpp index 23a6db9..919accc 100644 --- a/src/threading/Sink.hpp +++ b/src/threading/Sink.hpp @@ -38,10 +38,10 @@ class SinkBase virtual ~SinkBase() { } - virtual T get() = 0; + virtual T pop() = 0; virtual void inject(T const & value) = 0; - inline operator T() { return get(); } + inline operator T() { return pop(); } }; template<typename T> @@ -52,7 +52,7 @@ class SinkNonBlocking : public SinkBase<T> SinkNonBlocking() { } virtual ~SinkNonBlocking() { } - virtual T get() override; + virtual T pop() override; virtual void inject(T const & value) override; @@ -71,7 +71,7 @@ class SinkBlocking : public SinkBase<T> SinkBlocking(size_t const size); virtual ~SinkBlocking() { } - virtual T get() override; + virtual T pop() override; virtual void inject(T const & value) override; @@ -89,7 +89,7 @@ class SinkBlocking : public SinkBase<T> **************************************************************************************/ template<typename T> -T SinkNonBlocking<T>::get() +T SinkNonBlocking<T>::pop() { _mutex.lock(); return _data; @@ -116,7 +116,7 @@ SinkBlocking<T>::SinkBlocking(size_t const size) { } template<typename T> -T SinkBlocking<T>::get() +T SinkBlocking<T>::pop() { _mutex.lock(); while (_data.isEmpty()) diff --git a/src/threading/Source.hpp b/src/threading/Source.hpp index ebe531a..d4506f8 100644 --- a/src/threading/Source.hpp +++ b/src/threading/Source.hpp @@ -43,7 +43,7 @@ class Source public: void connectTo(SinkBase<T> & sink); - void set(T const & val); + void push(T const & val); void operator = (T const & val); private: @@ -61,7 +61,7 @@ void Source<T>::connectTo(SinkBase<T> & sink) } template<typename T> -void Source<T>::set(T const & val) +void Source<T>::push(T const & val) { std::for_each(std::begin(_sink_list), std::end (_sink_list), @@ -74,7 +74,7 @@ void Source<T>::set(T const & val) template<typename T> void Source<T>::operator = (T const & val) { - set(val); + push(val); } #endif /* ARDUINO_THREADS_SOURCE_HPP_ */ From 515ee2419219f3fd9f89bdd3235896b31a3f339e Mon Sep 17 00:00:00 2001 From: Alexander Entinger <cto@lxrobotics.com> Date: Mon, 20 Jun 2022 10:21:35 +0200 Subject: [PATCH 6/8] Mark functions utilizing operator overloading as deprected. --- src/threading/Shared.hpp | 5 ++--- src/threading/Sink.hpp | 5 ++++- src/threading/Source.hpp | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/threading/Shared.hpp b/src/threading/Shared.hpp index 9b7835c..45f70ae 100644 --- a/src/threading/Shared.hpp +++ b/src/threading/Shared.hpp @@ -42,11 +42,10 @@ class Shared T pop(); void push(T const & val); - - operator T(); - void operator = (T const & val); inline T peek() const { return _val; } + operator T() [[deprecated("Use 'pop()' instead.")]]; + void operator = (T const & val) [[deprecated("Use 'push()' instead.")]]; private: diff --git a/src/threading/Sink.hpp b/src/threading/Sink.hpp index 919accc..eb159b3 100644 --- a/src/threading/Sink.hpp +++ b/src/threading/Sink.hpp @@ -41,7 +41,10 @@ class SinkBase virtual T pop() = 0; virtual void inject(T const & value) = 0; - inline operator T() { return pop(); } + inline operator T() [[deprecated("Use 'pop()' instead.")]] + { + return pop(); + } }; template<typename T> diff --git a/src/threading/Source.hpp b/src/threading/Source.hpp index d4506f8..0f64af5 100644 --- a/src/threading/Source.hpp +++ b/src/threading/Source.hpp @@ -44,7 +44,8 @@ class Source void connectTo(SinkBase<T> & sink); void push(T const & val); - void operator = (T const & val); + + void operator = (T const & val) [[deprecated("Use 'push()' instead.")]]; private: std::list<SinkBase<T> *> _sink_list; From fc4930c26cc18637791beb23240d4fb9d07b65e1 Mon Sep 17 00:00:00 2001 From: Alexander Entinger <cto@lxrobotics.com> Date: Mon, 20 Jun 2022 10:26:25 +0200 Subject: [PATCH 7/8] Update examples to no longer use operator overloading (use push/pop instead). --- examples/Threading_Basics/Shared_Counter/Consumer.inot | 2 +- examples/Threading_Basics/Shared_Counter/Producer.inot | 2 +- examples/Threading_Basics/Source_Sink_Counter/Consumer.inot | 2 +- examples/Threading_Basics/Source_Sink_Counter/Producer.inot | 2 +- examples/Threading_Basics/Source_Sink_LED/Sink_Thread.inot | 2 +- examples/Threading_Basics/Source_Sink_LED/Source_Thread.inot | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/Threading_Basics/Shared_Counter/Consumer.inot b/examples/Threading_Basics/Shared_Counter/Consumer.inot index c03efd9..c6a89fd 100644 --- a/examples/Threading_Basics/Shared_Counter/Consumer.inot +++ b/examples/Threading_Basics/Shared_Counter/Consumer.inot @@ -12,5 +12,5 @@ void loop() * available, then this thread is suspended until new data * is available for reading. */ - Serial.println(counter); + Serial.println(counter.pop()); } diff --git a/examples/Threading_Basics/Shared_Counter/Producer.inot b/examples/Threading_Basics/Shared_Counter/Producer.inot index 7475653..59578d4 100644 --- a/examples/Threading_Basics/Shared_Counter/Producer.inot +++ b/examples/Threading_Basics/Shared_Counter/Producer.inot @@ -10,7 +10,7 @@ void loop() * 'counter'. Internally this is stored within a queue in a FIFO * (First-In/First-Out) manner. */ - counter = i; + counter.push(i); i++; delay(100); } diff --git a/examples/Threading_Basics/Source_Sink_Counter/Consumer.inot b/examples/Threading_Basics/Source_Sink_Counter/Consumer.inot index 5ae30b5..21aafec 100644 --- a/examples/Threading_Basics/Source_Sink_Counter/Consumer.inot +++ b/examples/Threading_Basics/Source_Sink_Counter/Consumer.inot @@ -9,5 +9,5 @@ void setup() void loop() { - Serial.println(counter); + Serial.println(counter.pop()); } diff --git a/examples/Threading_Basics/Source_Sink_Counter/Producer.inot b/examples/Threading_Basics/Source_Sink_Counter/Producer.inot index 3938a1c..c359c40 100644 --- a/examples/Threading_Basics/Source_Sink_Counter/Producer.inot +++ b/examples/Threading_Basics/Source_Sink_Counter/Producer.inot @@ -9,6 +9,6 @@ void setup() void loop() { static int i = 0; - counter = i; + counter.push(i); i++; } diff --git a/examples/Threading_Basics/Source_Sink_LED/Sink_Thread.inot b/examples/Threading_Basics/Source_Sink_LED/Sink_Thread.inot index 369de6b..e4dd975 100644 --- a/examples/Threading_Basics/Source_Sink_LED/Sink_Thread.inot +++ b/examples/Threading_Basics/Source_Sink_LED/Sink_Thread.inot @@ -12,5 +12,5 @@ void loop() * this call will block until new data is inserted from the connected SOURCE. This means * that the pace is dictated by the SOURCE that sends data every 100 ms. */ - digitalWrite(LED_BUILTIN, led); + digitalWrite(LED_BUILTIN, led.pop()); } diff --git a/examples/Threading_Basics/Source_Sink_LED/Source_Thread.inot b/examples/Threading_Basics/Source_Sink_LED/Source_Thread.inot index 78be7f4..dc8f864 100644 --- a/examples/Threading_Basics/Source_Sink_LED/Source_Thread.inot +++ b/examples/Threading_Basics/Source_Sink_LED/Source_Thread.inot @@ -8,8 +8,8 @@ void setup() void loop() { - led = true; + led.push(true); delay(100); - led = false; + led.push(false); delay(100); } From 948fcd3d1c4324dfd70e59d04a7d8d3175a10438 Mon Sep 17 00:00:00 2001 From: Alexander Entinger <cto@lxrobotics.com> Date: Mon, 20 Jun 2022 13:01:48 +0200 Subject: [PATCH 8/8] Update documentation re pop/push. --- docs/02-data-exchange.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/02-data-exchange.md b/docs/02-data-exchange.md index e30aeaf..5471f8e 100644 --- a/docs/02-data-exchange.md +++ b/docs/02-data-exchange.md @@ -18,14 +18,14 @@ New values can be inserted naturally by using the assignment operator `=` as if ```C++ /* Thread_1.inot */ -counter = 10; /* Store a value into the shared variable in a threadsafe manner. */ +counter.push(10); /* Store a value into the shared variable in a threadsafe manner. */ ``` If the internal queue is full the oldest element is discarded and the latest element is inserted into the queue. Retrieving stored data works also very naturally like it would for any POD data type: ```C++ /* Thread_2.inot */ -Serial.println(counter); /* Retrieves a value from the shared variable in a threadsafe manner. */ +Serial.println(counter.pop()); /* Retrieves a value from the shared variable in a threadsafe manner. */ ``` Should the internal queue be empty when trying to read the latest available value then the thread reading the shared variable will be suspended and the next available thread will be scheduled. Once a new value is stored inside the shared variable the suspended thread resumes operation and consumes the value which has been stored in the internal queue. @@ -55,16 +55,16 @@ DataProducerThread.counter.connectTo(DataConsumerThread_2.counter); Whenever a new value is assigned to a data source, i.e. ```C++ /* DataProducerThread.inot */ -counter = 10; +counter.push(10); ``` data is automatically copied and stored within the internal queues of all connected data sinks, from where it can be retrieved, i.e. ```C++ /* DataConsumerThread_1.inot */ -Serial.println(counter); +Serial.println(counter.pop()); ``` ```C++ /* DataConsumerThread_2.inot */ -Serial.println(counter); +Serial.println(counter.pop()); ``` If a thread tries to read from an empty `Sink` the thread is suspended and the next ready thread is scheduled. When a new value is written to a `Source` and consequently copied to a `Sink` the suspended thread is resumed and continuous execution (i.e. read the data and act upon it).