/* * threads_actor.hpp * * Created on: Jun 11, 2021 * Author: erki */ #ifndef SKULLC_THREADS_ACTOR_HPP_ #define SKULLC_THREADS_ACTOR_HPP_ #include #include #include #include #include #include "threads_primitivethread.hpp" #include "threads_signal.hpp" #include "threads_timer.hpp" namespace Threads { template class Actor { public: using value_type = std::variant; static_assert(std::is_default_constructible_v, "Ts must be default constructible."); static_assert(std::is_trivially_copyable_v, "Ts 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_(&Actor::threadHandler_, this, name, priority, stack_size), msg_queue_(xQueueCreate(queue_size, sizeof(value_type))), signal_(this) { assert(msg_queue_); } virtual ~Actor() {} template struct Signal : Signallable { using parent = Actor; static_assert(std::is_convertible_v, "value_type cannot be constructed from T."); explicit Signal(parent* q) : q_(q) {} Signal(const Signal&) = default; Signal(Signal&&) = default; Signal& operator=(const Signal&) = default; Signal& operator=(Signal&&) = default; void emit(const T& data) override { parent::value_type to_send = data; xQueueSend(q_->msg_queue_, &to_send, 0); } BaseType_t emitFromIsr(const T& data) override { parent::value_type to_send = data; BaseType_t was_awoken = pdFALSE; xQueueSendFromISR(q_->msg_queue_, &to_send, &was_awoken); return was_awoken; } private: parent* q_; }; template Signal getSignal() { static_assert(std::is_convertible_v, "value_type cannot be constructed from U."); return Signal(this); } template Signal* getAllocatedSignal() { static_assert(std::is_convertible_v, "value_type cannot be constructed from U."); return new Signal(this); } Signallable* getStaticSignal() { return &signal_; } protected: virtual void init() = 0; private: PrimitiveThread thread_; QueueHandle_t msg_queue_; struct Visitor_ { using parent = Actor; parent* q; Visitor_(parent* q) : q(q) {} template void operator()(const U& u) { CRTP* s = static_cast(q); s->onSignal(u); } }; void operator()() { init(); value_type data{}; while (true) { const BaseType_t got = xQueueReceive(msg_queue_, &data, portMAX_DELAY); if (got == pdTRUE) { std::visit(Visitor_{this}, data); } } } static void threadHandler_(void* d) { auto* dd = static_cast*>(d); (*dd)(); } Signal signal_; }; template class Actor { public: using value_type = void; 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(int))), signal_(*this) { assert(msg_queue_); } virtual ~Actor() {} Signallable* getSignal() { return &signal_; } protected: virtual void init() = 0; private: PrimitiveThread thread_; QueueHandle_t msg_queue_; void operator()() { init(); int data; while (true) { const BaseType_t got = xQueueReceive(msg_queue_, &data, portMAX_DELAY); if (got == pdTRUE) { auto* crtp = static_cast(this); crtp->onSignal(); } } } friend struct Signal_; struct Signal_ : Signallable { using parent = Actor; parent& q; explicit Signal_(parent& q) : q(q) {} void emit() override { const int data = 0; xQueueSend(q.msg_queue_, &data, 0); } BaseType_t emitFromIsr() override { const int data = 0; BaseType_t was_awoken = pdFALSE; xQueueSendFromISR(q.msg_queue_, &data, &was_awoken); return was_awoken; } }; Signal_ signal_; }; }// namespace Threads #endif /* SKULLC_THREADS_ACTOR_HPP_ */