From 6f7756e1cbdec822bbb3a36cb826b0ae80636991 Mon Sep 17 00:00:00 2001 From: Erki Date: Sat, 19 Jun 2021 19:34:28 +0300 Subject: [PATCH] Threads refactor Split thread into two different entities. Add exclusive signal. --- Threads/Inc/threads_exclusivesignal.hpp | 150 ++++++++++++++++++++++++ Threads/Inc/threads_primitivethread.hpp | 84 +++++++++++++ Threads/Inc/threads_thread.hpp | 101 ++-------------- Threads/Src/threads_primitivethread.cpp | 120 +++++++++++++++++++ 4 files changed, 363 insertions(+), 92 deletions(-) create mode 100644 Threads/Inc/threads_exclusivesignal.hpp create mode 100644 Threads/Inc/threads_primitivethread.hpp create mode 100644 Threads/Src/threads_primitivethread.cpp diff --git a/Threads/Inc/threads_exclusivesignal.hpp b/Threads/Inc/threads_exclusivesignal.hpp new file mode 100644 index 0000000..acf3a8e --- /dev/null +++ b/Threads/Inc/threads_exclusivesignal.hpp @@ -0,0 +1,150 @@ +/* + * threads_signal.hpp + * + * Created on: Jun 11, 2021 + * Author: erki + */ + +#ifndef SKULLC_THREADS_EXCLUSIVESIGNAL_HPP_ +#define SKULLC_THREADS_EXCLUSIVESIGNAL_HPP_ + +#include +#include +#include +#include + +#include "threads_primitivethread.hpp" + +namespace Threads +{ + +template +class ExclusiveSignal +{ +public: + using value_type = T; + using result_type = std::optional; + + static_assert( + std::is_trivially_copyable_v, "Signal's value must be trivially copyable."); + + ExclusiveSignal() + : current_value_(std::nullopt) + , waiting_thread_(std::nullopt) + { } + + ExclusiveSignal(const ExclusiveSignal&) = delete; + ExclusiveSignal(ExclusiveSignal&&) = delete; + ExclusiveSignal& operator=(const ExclusiveSignal&) = delete; + ExclusiveSignal& operator=(ExclusiveSignal&&) = delete; + +#ifdef INCLUDE_xTaskGetCurrentTaskHandle + result_type wait() + { + return wait(PrimitiveThread::getCurrentThread()); + } + + result_type wait(const long timeout) + { + return wait(PrimitiveThread::getCurrentThread(), timeout); + } +#endif + + result_type wait(const PrimitiveThread& currentThread) + { + return wait(currentThread, portMAX_DELAY); + } + + result_type wait(const PrimitiveThread& currentThread, const long timeout) + { + waiting_thread_ = currentThread; + + const bool notified = currentThread.notifyWait(timeout); + + if (!notified) + { + return std::nullopt; + } + else + { + return current_value_; + } + } + + bool emit(const result_type& v) + { + current_value_ = v; + + if (!waiting_thread_) + { + return false; + } + else + { + return waiting_thread_->notify(0, eNoAction); + } + } + +private: + result_type current_value_; + std::optional waiting_thread_; +}; + +template<> +struct ExclusiveSignal +{ + using value_type = void; + using result_type = bool; + + ExclusiveSignal() + : waiting_thread_(std::nullopt) + { } + + ExclusiveSignal(const ExclusiveSignal&) = delete; + ExclusiveSignal(ExclusiveSignal&&) = delete; + ExclusiveSignal& operator=(const ExclusiveSignal&) = delete; + ExclusiveSignal& operator=(ExclusiveSignal&&) = delete; + +#ifdef INCLUDE_xTaskGetCurrentTaskHandle + result_type wait() + { + return wait(PrimitiveThread::getCurrentThread()); + } + + result_type wait(const long timeout) + { + return wait(PrimitiveThread::getCurrentThread(), timeout); + } +#endif + + result_type wait(PrimitiveThread currentThread) + { + return wait(currentThread, portMAX_DELAY); + } + + result_type wait(PrimitiveThread currentThread, const long timeout) + { + waiting_thread_ = currentThread; + return currentThread.notifyWait(timeout); + } + + bool emit() + { + if (!waiting_thread_) + { + return false; + } + else + { + return waiting_thread_->notify(0, eNoAction); + } + } + +private: + std::optional waiting_thread_; +}; + +} + + +#endif /* SKULLC_THREADS_EXCLUSIVESIGNAL_HPP_ */ diff --git a/Threads/Inc/threads_primitivethread.hpp b/Threads/Inc/threads_primitivethread.hpp new file mode 100644 index 0000000..69eb7a6 --- /dev/null +++ b/Threads/Inc/threads_primitivethread.hpp @@ -0,0 +1,84 @@ +/* + * threads_basethread.hpp + * + * Created on: Jun 11, 2021 + * Author: erki + */ + +#ifndef SKULLC_THREADS_PRIMITIVETHREAD_HPP_ +#define SKULLC_THREADS_PRIMITIVETHREAD_HPP_ + +#include +#include + +#include +#include + +namespace Threads +{ + +class PrimitiveThread +{ +public: + PrimitiveThread() = delete; + PrimitiveThread(const PrimitiveThread&) = default; + PrimitiveThread(PrimitiveThread&&) = default; + PrimitiveThread& operator=(const PrimitiveThread&) = default; + PrimitiveThread& operator=(PrimitiveThread&&) = default; + + osThreadId_t thread_id; + osThreadAttr_t attributes; + +#ifdef INCLUDE_xTaskGetCurrentTaskHandle + static PrimitiveThread getCurrentThread(); +#endif + + [[nodiscard]] constexpr TaskHandle_t taskHandle() const + { + return static_cast(thread_id); + } + + [[nodiscard]] BaseType_t notify(const long value, const eNotifyAction action); + + /** + * + * @returns Tuple. First member indicating the return value of the underlying notify + * function, the second member indicating whether a higher priority task was awoken. + * + */ + [[nodiscard]] std::pair notifyFromIsr(const long value, const eNotifyAction action); + + void yield(); + void sleep(const unsigned long delay); + void sleepUntil(const unsigned long deadline); + + [[nodiscard]] bool notifyWait(const TickType_t delay) const; + [[nodiscard]] std::pair notifyWait(const TickType_t delay, const unsigned long set_on_entry, const unsigned long set_on_exit) const; + +protected: + explicit PrimitiveThread(osThreadFunc_t function, const osThreadAttr_t& attrs); + + template + PrimitiveThread(F runner, const char* name, const osPriority_t priority, const std::uint32_t stack_size) + : PrimitiveThread(runner, constructThreadAttrs_(name, priority, stack_size)) + { + static_assert(std::is_convertible_v, "Run function F is not convertible to osThreadFunc_t (void (void*))."); + } + +private: + explicit PrimitiveThread(TaskHandle_t threadHandle); + osThreadAttr_t constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size); +}; + +inline bool operator==(const PrimitiveThread& lhs, const PrimitiveThread& rhs) +{ + if (!lhs.thread_id || !rhs.thread_id) + return false; + + return lhs.thread_id == rhs.thread_id; +} + +} + + +#endif /* SKULLC_THREADS_PRIMITIVETHREAD_HPP_ */ diff --git a/Threads/Inc/threads_thread.hpp b/Threads/Inc/threads_thread.hpp index 4ad267d..0afd056 100644 --- a/Threads/Inc/threads_thread.hpp +++ b/Threads/Inc/threads_thread.hpp @@ -8,110 +8,27 @@ #ifndef SKULLC_THREADS_THREAD_HPP_ #define SKULLC_THREADS_THREAD_HPP_ -#include - -#include - -#include -#include -#include - -#include +#include "threads_primitivethread.hpp" namespace Threads { template -class Thread +class Thread : public PrimitiveThread { public: - Thread(const osThreadAttr_t attrs) - : attributes(attrs) - { - auto f = [](void* d) { - CRTP* dd = static_cast(d); - (*dd)(); - }; - - thread_id = osThreadNew(f, this, &attributes); - assert(thread_id != nullptr); - } - Thread(const char* name, const osPriority_t priority, const std::uint32_t stack_size) - : Thread(constructThreadAttrs_(name, priority, stack_size)) + : PrimitiveThread([](void* d) { + CRTP* dd = static_cast(d); + (*dd)(); + }, name, priority, stack_size) {} + Thread() = delete; Thread(const Thread&) = delete; Thread(Thread&&) = delete; - - osThreadId_t thread_id; - osThreadAttr_t attributes; - - constexpr TaskHandle_t taskHandle() const - { - return static_cast(thread_id); - } - - [[nodiscard]] BaseType_t notify(const long value, const eNotifyAction action) - { - return xTaskNotify(taskHandle(), value, action); - } - - /** - * - * @returns Tuple. First member indicating the return value of the underlying notify - * function, the second member indicating whether a higher priority task was awoken. - * - */ - [[nodiscard]] std::pair notifyFromIsr(const long value, const eNotifyAction action) - { - BaseType_t was_notified = pdFALSE; - - const BaseType_t rval = xTaskNotifyFromISR(taskHandle(), value, action, &was_notified); - - return {rval, was_notified}; - } - - void yield() - { - osThreadYield(); - } - - void sleep(const unsigned long delay) - { - osDelay(delay); - } - - void sleepUntil(const unsigned long deadline) - { - osDelayUntil(deadline); - } - -protected: - bool notifyWait(const TickType_t delay) const - { - return xTaskNotifyWait(0, std::numeric_limits::max(), nullptr, delay); - } - - std::pair notifyWait(const TickType_t delay, const unsigned long set_on_entry, const unsigned long set_on_exit) const - { - unsigned long notification_value = 0; - const bool rval = xTaskNotifyWait(set_on_entry, set_on_exit, ¬ification_value, delay); - - return {rval, notification_value}; - } - -private: - osThreadAttr_t constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size) - { - auto attrs = Peripherals::zeroInitialized(); - - attrs.name = name; - attrs.priority = priority; - attrs.stack_size = stack_size; - - return attrs; - } + Thread& operator=(const Thread&) = delete; + Thread& operator=(Thread&&) = delete; }; }// namespace Threads diff --git a/Threads/Src/threads_primitivethread.cpp b/Threads/Src/threads_primitivethread.cpp new file mode 100644 index 0000000..1e1c8b2 --- /dev/null +++ b/Threads/Src/threads_primitivethread.cpp @@ -0,0 +1,120 @@ +/* + * threads_primitivethread.cpp + * + * Created on: Jun 11, 2021 + * Author: erki + */ + +#include "threads_primitivethread.hpp" + +#include "peripherals_utility.hpp" + +#ifdef INCLUDE_xTaskGetCurrentTaskHandle +# define ASSERT_IS_CURRENT() \ + assert(Threads::PrimitiveThread::getCurrentThread().taskHandle() == this->taskHandle()) +#else +# define ASSERT_IS_CURRENT() +#endif + +#ifdef INCLUDE_xTaskGetCurrentTaskHandle +# define ASSERT_IS_NOT_CURRENT() \ + assert(Threads::PrimitiveThread::getCurrentThread().taskHandle() != this->taskHandle()) +#else +# define ASSERT_IS_NOT_CURRENT() +#endif + +namespace Threads +{ + +#ifdef INCLUDE_xTaskGetCurrentTaskHandle +PrimitiveThread PrimitiveThread::getCurrentThread() +{ + return PrimitiveThread(xTaskGetCurrentTaskHandle()); +} +#endif + +BaseType_t PrimitiveThread::notify(const long value, const eNotifyAction action) +{ + ASSERT_IS_NOT_CURRENT(); + + return xTaskNotify(taskHandle(), value, action); +} + +std::pair PrimitiveThread::notifyFromIsr(const long value, const eNotifyAction action) +{ + BaseType_t was_notified = pdFALSE; + + const BaseType_t rval = xTaskNotifyFromISR(taskHandle(), value, action, &was_notified); + + return {rval, was_notified}; +} + +void PrimitiveThread::yield() +{ + ASSERT_IS_CURRENT(); + + osThreadYield(); +} + +void PrimitiveThread::sleep(const unsigned long delay) +{ + ASSERT_IS_CURRENT(); + + osDelay(delay); +} + +void PrimitiveThread::sleepUntil(const unsigned long deadline) +{ + ASSERT_IS_CURRENT(); + + osDelayUntil(deadline); +} + +[[nodiscard]] bool PrimitiveThread::notifyWait(const TickType_t delay) const +{ + ASSERT_IS_CURRENT(); + + return xTaskNotifyWait(0, std::numeric_limits::max(), nullptr, delay); +} + +[[nodiscard]] std::pair PrimitiveThread::notifyWait(const TickType_t delay, const unsigned long set_on_entry, const unsigned long set_on_exit) const +{ + ASSERT_IS_CURRENT(); + + unsigned long notification_value = 0; + const bool rval = xTaskNotifyWait(set_on_entry, set_on_exit, ¬ification_value, delay); + + return {rval, notification_value}; +} + + +PrimitiveThread::PrimitiveThread(osThreadFunc_t function, const osThreadAttr_t& attrs) + : thread_id(nullptr) + , attributes(attrs) +{ + assert(function); + + thread_id = osThreadNew(function, this, &attributes); + assert(thread_id != nullptr); +} + + +PrimitiveThread::PrimitiveThread(TaskHandle_t threadHandle) + : thread_id(static_cast(threadHandle)) + , attributes(Peripherals::zeroInitialized()) +{ + assert(thread_id != nullptr); +} + +osThreadAttr_t PrimitiveThread::constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size) +{ + auto attrs = Peripherals::zeroInitialized(); + + attrs.name = name; + attrs.priority = priority; + attrs.stack_size = stack_size; + + return attrs; +} + +}