/* * 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" #include "threads_actor_thread.hpp" namespace Threads { class IActor { public: IActor() = delete; IActor(ActorThread* thread) { moveToThread(thread); } virtual ~IActor() { } virtual void init() { } virtual void doWork() = 0; virtual bool hasWork() = 0; void moveToThread(ActorThread* thread) { assert(thread); if (thread_) thread_->removeActor_(this); thread->acceptActor_(this); thread_ = thread; } protected: ActorThread* thread_ = nullptr; }; template class Actor : public IActor { 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, ActorThread* thread) : IActor(thread), msg_queue_(xQueueCreate(queue_size, sizeof(value_type))), signal_(this) { assert(msg_queue_); } virtual ~Actor() {} void doWork() override { value_type data{}; const BaseType_t got = xQueueReceive(msg_queue_, &data, 0); if (got == pdTRUE) { std::visit(Visitor_{this}, data); } } bool hasWork() override { return uxQueueMessagesWaiting(msg_queue_) > 0; } 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) {} 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); } Signallable* getStaticSignal() { return &signal_; } private: 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); } }; Signal signal_; }; template class Actor : public IActor { public: using value_type = void; Actor(const std::uint32_t queue_size, ActorThread* thread) : IActor(thread), msg_queue_(xQueueCreate(queue_size, sizeof(int))), signal_(*this) { assert(msg_queue_); } virtual ~Actor() {} void doWork() override { int data{}; const BaseType_t got = xQueueReceive(msg_queue_, &data, 0); if (got == pdTRUE) { auto* crtp = static_cast(this); crtp->onSignal(); } } bool hasWork() override { return uxQueueMessagesWaiting(msg_queue_) > 0; } Signallable* getSignal() { return &signal_; } private: QueueHandle_t msg_queue_; 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_ */