123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // SPDX-License-Identifier: MIT
- // SPDX-FileCopyrightText: 2022 Ivan Baidakou
- #pragma once
- #include "actor.hpp"
- #include "message.hpp"
- #include "planner.hpp"
- #include "queue.hpp"
- #include "rotor-light-bsp.h"
- #include <tuple>
- #include <type_traits>
- #include <utility>
- namespace rotor_light {
- namespace details {
- /** \struct ChildState
- * \brief wrapper for holding child states in supervisor */
- struct ChildState {
- /** from which state actor is asked to shut self down */
- State down_state = State::off;
- };
- } // namespace details
- /** \struct SupervisorBase
- * \brief base interface and implementation for all supervisors */
- struct SupervisorBase : ActorBase {
- friend struct ActorBase;
- /** the minimum amount of handles, which should
- * a derived actor class pre-allocate */
- static constexpr size_t min_handlers_amount =
- ActorBase::min_handlers_amount + 2;
- /** convenient alias to the parent struct */
- using Parent = ActorBase;
- /** ctor */
- SupervisorBase(char *backends, size_t backends_count, ActorBase **actors,
- details::ChildState *states, size_t actors_count);
- /** returns meta-queue */
- inline QueueBase *get_queue() { return queue; }
- void initialize() override;
- /**
- * @brief starts all actors (initialize, wait confirmation etc.)
- * @param poll_timer whether `RefreshTime` message should be
- * send self on idle. When it is, the `process` method will never
- * exit in normal circumstances
- */
- void start(bool poll_timer = false);
- /**
- * Binds context to the supervisor and all its children. Permanent
- * actorIds are created and assigned during this phase.
- *
- * This method should be invoked only once in lifetime
- * of actors.
- *
- */
- virtual void bind(Context &context, ActorId value = 0);
- uint8_t bind(ActorId initial_value, SupervisorBase *supervisor,
- Context &context) override;
- /**
- *
- * "Main loop" of the framework, which does messages dispatching.
- * As the new messages are usually generated during messages dispatching
- * this method never exits under normal circumstances.
- *
- * It can exit in two cases:
- *
- * 1. There are no more messages. This might happen if the endless
- * timer polling is enabled in `start` method. This means, that
- * you intentionally do that, e.g. to enter into a sleep mode and do
- * some power-saving before the next event. Platform-specific
- * sleep and awake codes should be implemented to achive that.
- *
- * 2. Supervisor has terminated, and there is no sense of further
- * restart attempts. Usually that means, that supervisor did its
- * best, all restarts (having sense) has been applied without effect,
- * and nothing can be done, possibly due to unrecoverable hardware
- * failure; or the supervisor has terminated due to its job is done.
- * Usually that means board restart or poweroff.
- *
- */
- void process();
- // handlers
- void advance_init() override;
- void advance_start() override;
- void advance_stop() override;
- /** returns pointer to the planner */
- inline PlannerBase *get_planner() { return planner; }
- protected:
- /**
- * refreshes time, processes expired time events and re-schedules
- * `RefreshTime` message
- *
- * The method can be overriden to do something useful "on idle".
- *
- */
- virtual void on_refhesh_timer(message::RefreshTime &);
- /** pointer to child actor holding pointers */
- ActorBase **actors;
- /** pointer to child actor states */
- details::ChildState *states;
- /** the amount of child actors */
- size_t actors_count;
- /** pointer to the master queue */
- QueueBase *queue;
- /** pointer to the planner */
- PlannerBase *planner;
- /** function pointer, which can return "now" */
- NowFunction now;
- private:
- void on_state_change_ack(message::ChangeStateAck &);
- void on_child_init_failure(size_t actor_index);
- void dispatch(Message &);
- void on_child_down(size_t actor_index);
- void init_child(size_t actor_index);
- void start_actor(size_t actor_index);
- size_t process_watchdog();
- bool check_child_state(State subject, bool skip_self);
- };
- /** \struct Supervisor
- * \brief convenient templated base class for supervisor, which
- * owns (stores) all it's child-actors */
- template <size_t HandlersCount, typename... Actors>
- struct Supervisor : SupervisorBase {
- static_assert(HandlersCount >= SupervisorBase::min_handlers_amount,
- "no enough handlers");
- Supervisor()
- : SupervisorBase(reinterpret_cast<char *>(&backends), HandlersCount,
- actors, children_states_holder, children_count + 1) {
- actors[0] = this;
- fill_actor<0>();
- }
- /** returns child actor by indes */
- template <size_t ChildIndex> auto get_child() {
- return &std::get<ChildIndex>(children);
- }
- protected:
- /** heterogenious / type-friendly container for child actors (type) */
- using ActorsList = std::tuple<Actors...>;
- /** count of child actors */
- static constexpr size_t children_count = std::tuple_size_v<ActorsList>;
- /** heterogenious / type-friendly container for child actors */
- ActorsList children;
- /** message handlers storage */
- ActorBase::Handler backends[HandlersCount];
- /** pointers to actors, including self. Needed to be processed
- * uniformly */
- ActorBase *actors[children_count + 1];
- /** child actors states, including self */
- details::ChildState children_states_holder[children_count + 1];
- private:
- template <size_t ChildIndex> void fill_actor() {
- if constexpr (ChildIndex < children_count) {
- actors[ChildIndex + 1] = get_child<ChildIndex>();
- fill_actor<ChildIndex + 1>();
- }
- }
- };
- template <typename Ctx, typename MessageType, typename... Args>
- bool ActorBase::send(size_t queue_index, Args... args) {
- auto &queue = supervisor->queue;
- if constexpr (std::is_same_v<Ctx, ctx::thread>) {
- ROTOR_LIGHT_DISABLE_INTERRUPTS();
- }
- auto result =
- queue->put<MessageType>(queue_index, std::forward<Args>(args)...);
- if constexpr (std::is_same_v<Ctx, ctx::thread>) {
- ROTOR_LIGHT_ENABLE_INTERRUPTS();
- }
- return result;
- }
- template <typename Ctx>
- EventId ActorBase::add_event(Duration delta, Callback callback, void *data) {
- assert(supervisor->now && supervisor->planner);
- auto when = supervisor->now() + delta;
- return supervisor->planner->add_event<Ctx>(when, callback, data);
- }
- } // namespace rotor_light
|