diff --git a/Threads/Inc/threads_actor.hpp b/Threads/Inc/threads_actor.hpp new file mode 100644 index 0000000..f5f94bd --- /dev/null +++ b/Threads/Inc/threads_actor.hpp @@ -0,0 +1,102 @@ +/* + * threads_actor.hpp + * + * Created on: Jun 11, 2021 + * Author: erki + */ + +#ifndef SKULLC_THREADS_ACTOR_HPP_ +#define SKULLC_THREADS_ACTOR_HPP_ + +#include +#include + +#include "threads_primitivethread.hpp" +#include "threads_signal.hpp" + +namespace Threads +{ + +template +class Actor +{ +public: + using value_type = T; + + static_assert(std::is_trivially_constructible_v, "T must be trivially constructible."); + static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); + + Actor(const std::uint32_t queue_size, const char* name, const osPriority_t priority, const std::uint32_t stack_size) + : thread_([](void* d) { + auto* dd = static_cast*>(d); + (*dd)(); + }, + this, name, priority, stack_size), + msg_queue_(xQueueCreate(queue_size, sizeof(value_type))), signal_(*this) + { + assert(msg_queue_); + } + + Signallable* getSignal() + { + return &signal_; + } + +protected: + virtual void init() = 0; + virtual void onSignal(const T& data) = 0; + +private: + PrimitiveThread thread_; + QueueHandle_t msg_queue_; + + void operator()() + { + init(); + + value_type data; + + while (true) + { + const BaseType_t got = xQueueReceive(msg_queue_, &data, portMAX_DELAY); + + if (got) + { + onSignal(data); + } + } + } + + friend struct Signal_; + + struct Signal_ : Signallable + { + using parent = Actor; + parent& q; + + explicit Signal_(parent& q) + : q(q) + {} + + bool emit(const value_type& data) override + { + const BaseType_t success = xQueueSend(q.msg_queue_, &data, 0); + return success == pdTRUE; + } + + std::pair emitFromIsr(const value_type& data) override + { + BaseType_t was_awoken = pdFALSE; + const BaseType_t success = xQueueSendFromISR(q.msg_queue_, &data, &was_awoken); + + return {success == pdTRUE, was_awoken}; + } + }; + + Signal_ signal_; +}; + +}// namespace Threads + + +#endif /* SKULLC_THREADS_ACTOR_HPP_ */ diff --git a/Threads/Inc/threads_exclusivesignal.hpp b/Threads/Inc/threads_exclusivesignal.hpp index acf3a8e..a51ec0e 100644 --- a/Threads/Inc/threads_exclusivesignal.hpp +++ b/Threads/Inc/threads_exclusivesignal.hpp @@ -14,24 +14,25 @@ #include #include "threads_primitivethread.hpp" +#include "threads_signal.hpp" namespace Threads { template -class ExclusiveSignal +class ExclusiveSignal : public Signallable + , public Awaitable> { public: using value_type = T; using result_type = std::optional; static_assert( - std::is_trivially_copyable_v, "Signal's value must be trivially copyable."); + std::is_trivially_copyable_v, "Signal's value must be trivially copyable."); ExclusiveSignal() - : current_value_(std::nullopt) - , waiting_thread_(std::nullopt) - { } + : current_value_(std::nullopt), waiting_thread_(std::nullopt) + {} ExclusiveSignal(const ExclusiveSignal&) = delete; ExclusiveSignal(ExclusiveSignal&&) = delete; @@ -39,23 +40,23 @@ public: ExclusiveSignal& operator=(ExclusiveSignal&&) = delete; #ifdef INCLUDE_xTaskGetCurrentTaskHandle - result_type wait() + result_type await() override { - return wait(PrimitiveThread::getCurrentThread()); + return await(PrimitiveThread::getCurrentThread()); } - result_type wait(const long timeout) + result_type await(const long timeout) override { - return wait(PrimitiveThread::getCurrentThread(), timeout); + return await(PrimitiveThread::getCurrentThread(), timeout); } #endif - result_type wait(const PrimitiveThread& currentThread) + result_type await(const PrimitiveThread& currentThread) override { - return wait(currentThread, portMAX_DELAY); + return await(currentThread, portMAX_DELAY); } - result_type wait(const PrimitiveThread& currentThread, const long timeout) + result_type await(const PrimitiveThread& currentThread, const long timeout) override { waiting_thread_ = currentThread; @@ -64,41 +65,56 @@ public: if (!notified) { return std::nullopt; - } - else + } else { return current_value_; } } - bool emit(const result_type& v) + bool emit(const value_type& v) override { current_value_ = v; if (!waiting_thread_) { return false; - } - else + } else { return waiting_thread_->notify(0, eNoAction); } } + std::pair emitFromIsr(const value_type& v) override + { + current_value_ = v; + + if (!waiting_thread_) + { + return {false, pdFALSE}; + } else + { + auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction); + (void) discard; + + return {true, was_notified}; + } + } + private: result_type current_value_; std::optional waiting_thread_; }; template<> -struct ExclusiveSignal +struct ExclusiveSignal : public Signallable + , public Awaitable { using value_type = void; using result_type = bool; ExclusiveSignal() - : waiting_thread_(std::nullopt) - { } + : waiting_thread_(std::nullopt) + {} ExclusiveSignal(const ExclusiveSignal&) = delete; ExclusiveSignal(ExclusiveSignal&&) = delete; @@ -106,45 +122,58 @@ struct ExclusiveSignal ExclusiveSignal& operator=(ExclusiveSignal&&) = delete; #ifdef INCLUDE_xTaskGetCurrentTaskHandle - result_type wait() + result_type await() override { - return wait(PrimitiveThread::getCurrentThread()); + return await(PrimitiveThread::getCurrentThread()); } - result_type wait(const long timeout) + result_type await(const long timeout) override { - return wait(PrimitiveThread::getCurrentThread(), timeout); + return await(PrimitiveThread::getCurrentThread(), timeout); } #endif - result_type wait(PrimitiveThread currentThread) + result_type await(const PrimitiveThread& currentThread) override { - return wait(currentThread, portMAX_DELAY); + return await(currentThread, portMAX_DELAY); } - result_type wait(PrimitiveThread currentThread, const long timeout) + result_type await(const PrimitiveThread& currentThread, const long timeout) override { waiting_thread_ = currentThread; return currentThread.notifyWait(timeout); } - bool emit() + bool emit() override { if (!waiting_thread_) { return false; - } - else + } else { return waiting_thread_->notify(0, eNoAction); } } + std::pair emitFromIsr() override + { + if (!waiting_thread_) + { + return {false, pdFALSE}; + } else + { + auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction); + (void) discard; + + return {true, was_notified}; + } + } + private: std::optional waiting_thread_; }; -} +}// namespace Threads #endif /* SKULLC_THREADS_EXCLUSIVESIGNAL_HPP_ */ diff --git a/Threads/Inc/threads_primitivethread.hpp b/Threads/Inc/threads_primitivethread.hpp index 69eb7a6..8be7ffc 100644 --- a/Threads/Inc/threads_primitivethread.hpp +++ b/Threads/Inc/threads_primitivethread.hpp @@ -11,8 +11,8 @@ #include #include -#include #include +#include namespace Threads { @@ -26,6 +26,15 @@ public: PrimitiveThread& operator=(const PrimitiveThread&) = default; PrimitiveThread& operator=(PrimitiveThread&&) = default; + explicit PrimitiveThread(osThreadFunc_t function, void* runner_data, const osThreadAttr_t& attrs); + + template + PrimitiveThread(F runner, void* runner_data, const char* name, const osPriority_t priority, const std::uint32_t stack_size) + : PrimitiveThread(runner, runner_data, constructThreadAttrs_(name, priority, stack_size)) + { + static_assert(std::is_convertible_v, "Run function F is not convertible to osThreadFunc_t (void (void*))."); + } + osThreadId_t thread_id; osThreadAttr_t attributes; @@ -55,17 +64,9 @@ public: [[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: + void* runner_data_ = nullptr; + explicit PrimitiveThread(TaskHandle_t threadHandle); osThreadAttr_t constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size); }; @@ -78,7 +79,7 @@ inline bool operator==(const PrimitiveThread& lhs, const PrimitiveThread& rhs) return lhs.thread_id == rhs.thread_id; } -} +}// namespace Threads #endif /* SKULLC_THREADS_PRIMITIVETHREAD_HPP_ */ diff --git a/Threads/Inc/threads_signal.hpp b/Threads/Inc/threads_signal.hpp new file mode 100644 index 0000000..16dd49a --- /dev/null +++ b/Threads/Inc/threads_signal.hpp @@ -0,0 +1,54 @@ +/* + * threads_signal.hpp + * + * Created on: Jun 20, 2021 + * Author: erki + */ + +#ifndef THREADS_INC_THREADS_SIGNAL_HPP_ +#define THREADS_INC_THREADS_SIGNAL_HPP_ + +#include + +namespace Threads +{ + +class PrimitiveThread; + +template +struct Signallable +{ + using value_type = T; + + static_assert(std::is_trivially_constructible_v, "T must be trivially constructible."); + + virtual bool emit(const T& t) = 0; + virtual std::pair emitFromIsr(const T& t) = 0; +}; + +template<> +struct Signallable +{ + using value_type = void; + + virtual bool emit() = 0; + virtual std::pair emitFromIsr() = 0; +}; + +template +struct Awaitable +{ + using result_type = R; + +#ifdef INCLUDE_xTaskGetCurrentTaskHandle + virtual result_type await() = 0; + virtual result_type await(const long timeout) = 0; +#endif + + virtual result_type await(const PrimitiveThread& currentThread) = 0; + virtual result_type await(const PrimitiveThread& currentThread, const long timeout) = 0; +}; + +}// namespace Threads + +#endif /* THREADS_INC_THREADS_SIGNAL_HPP_ */ diff --git a/Threads/Inc/threads_thread.hpp b/Threads/Inc/threads_thread.hpp index 0afd056..cda5924 100644 --- a/Threads/Inc/threads_thread.hpp +++ b/Threads/Inc/threads_thread.hpp @@ -21,7 +21,8 @@ public: : PrimitiveThread([](void* d) { CRTP* dd = static_cast(d); (*dd)(); - }, name, priority, stack_size) + }, + this, name, priority, stack_size) {} Thread() = delete; diff --git a/Threads/Src/threads_primitivethread.cpp b/Threads/Src/threads_primitivethread.cpp index 1e1c8b2..9d036ad 100644 --- a/Threads/Src/threads_primitivethread.cpp +++ b/Threads/Src/threads_primitivethread.cpp @@ -10,17 +10,17 @@ #include "peripherals_utility.hpp" #ifdef INCLUDE_xTaskGetCurrentTaskHandle -# define ASSERT_IS_CURRENT() \ +#define ASSERT_IS_CURRENT() \ assert(Threads::PrimitiveThread::getCurrentThread().taskHandle() == this->taskHandle()) #else -# define ASSERT_IS_CURRENT() +#define ASSERT_IS_CURRENT() #endif #ifdef INCLUDE_xTaskGetCurrentTaskHandle -# define ASSERT_IS_NOT_CURRENT() \ +#define ASSERT_IS_NOT_CURRENT() \ assert(Threads::PrimitiveThread::getCurrentThread().taskHandle() != this->taskHandle()) #else -# define ASSERT_IS_NOT_CURRENT() +#define ASSERT_IS_NOT_CURRENT() #endif namespace Threads @@ -88,20 +88,18 @@ void PrimitiveThread::sleepUntil(const unsigned long deadline) } -PrimitiveThread::PrimitiveThread(osThreadFunc_t function, const osThreadAttr_t& attrs) - : thread_id(nullptr) - , attributes(attrs) +PrimitiveThread::PrimitiveThread(osThreadFunc_t function, void* runner_data, const osThreadAttr_t& attrs) + : thread_id(nullptr), attributes(attrs), runner_data_(runner_data) { assert(function); - thread_id = osThreadNew(function, this, &attributes); + thread_id = osThreadNew(function, runner_data_, &attributes); assert(thread_id != nullptr); } PrimitiveThread::PrimitiveThread(TaskHandle_t threadHandle) - : thread_id(static_cast(threadHandle)) - , attributes(Peripherals::zeroInitialized()) + : thread_id(static_cast(threadHandle)), attributes(Peripherals::zeroInitialized()) { assert(thread_id != nullptr); } @@ -117,4 +115,4 @@ osThreadAttr_t PrimitiveThread::constructThreadAttrs_(const char* name, const os return attrs; } -} +}// namespace Threads