actor.hpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // SPDX-License-Identifier: MIT
  2. // SPDX-FileCopyrightText: 2022-2024 Ivan Baidakou
  3. #pragma once
  4. #include "context.hpp"
  5. #include "definitions.hpp"
  6. #include "messages.hpp"
  7. #include <cassert>
  8. namespace rotor_light {
  9. struct SupervisorBase;
  10. struct QueueBase;
  11. /** \struct ActorBase
  12. * \brief base interface and implementation for all actors, including
  13. * supervisors */
  14. struct ActorBase {
  15. /// alias for generic message handler
  16. using GenericMethod = void (ActorBase::*)(Message &);
  17. /** \struct handler_traits
  18. * \brief helps method signature extraction */
  19. template <typename T> struct handler_traits {};
  20. template <typename A, typename M> struct handler_traits<void (A::*)(M &)> {
  21. /// messsage type
  22. using FinalMessage = M;
  23. /// final actor type
  24. using Backend = A;
  25. };
  26. struct Handler;
  27. /// alias for method entry, enough to actually perfrom message delivery
  28. using MethodEntry = void (*)(Message &, ActorBase &, Handler &);
  29. /** \struct Handler
  30. * \brief runtime info about message subscription handler */
  31. struct Handler {
  32. /// holds pointer-to-member-function for message processing
  33. GenericMethod method;
  34. /// Method entry item
  35. MethodEntry entry;
  36. /// holds message_id to do runtime message matching
  37. MessageTypeId message_id;
  38. };
  39. /** the minimum amount of handles, which should
  40. * a derived actor class pre-allocate */
  41. static constexpr size_t min_handlers_amount = 1;
  42. ActorBase(const ActorBase &) = delete;
  43. ActorBase(ActorBase &&) = delete;
  44. /**
  45. * Perform actor intialization, namely subscription to methods.
  46. * Can ba invoked multiple times, if the actor is restarted several times.
  47. */
  48. virtual void initialize();
  49. /** assign actor id, supervisor and context. Performed only ONCE per
  50. * program lifetime */
  51. virtual uint8_t bind(ActorId initial_value, SupervisorBase *supervisor,
  52. Context &context);
  53. /** returns actor id */
  54. inline ActorId get_id() const { return id; }
  55. /** returns current state of the actor */
  56. inline State get_state() const { return state; }
  57. /** returns fail policy of the actor */
  58. inline FailPolicy get_fail_policy() const { return fail_policy; }
  59. /** set actor fail policy */
  60. inline void set_fail_policy(FailPolicy value) { fail_policy = value; }
  61. /** sends self a shutdown request message. The actor is NOT immediatly
  62. * stopped. */
  63. void stop();
  64. /** adds event, which will trigger in THREAD context after the
  65. * specified duration by invoking callbacke with the supplied data.
  66. *
  67. * The templated ctx is either ctx::thread (masks/unmasks interrupts)
  68. * or ctx::interrupt (does nothing).
  69. */
  70. template <typename Ctx>
  71. EventId add_event(Duration delta, Callback callback, void *data);
  72. /** cancels previously created event */
  73. void cancel_event(EventId event_id);
  74. /** \brief tries to emplace new message into appropriate queue
  75. *
  76. * Creates a new messsage of the specified type "in-place" of
  77. * the specified queue/priority; all supplied args are forwarded
  78. * to the message contructor
  79. *
  80. * Returns true if the message was succesfully created, and
  81. * false otherwise, i.e. if the queue is full.
  82. */
  83. template <typename Ctx, typename MessageType, typename... Args>
  84. bool send(size_t queue_index, Args &&...args) {
  85. return send_impl<Ctx, MessageType, false>(queue_index,
  86. std::forward<Args>(args)...);
  87. }
  88. /** \brief emplaces new message into appropriate queue
  89. *
  90. * It behaves the same as `send` method, with the exception
  91. * that the new message is **always** put into the queue.
  92. * If the queue is full, then the oldes message is discarded
  93. * and the new message is emplaced instead of it.
  94. *
  95. * Returns true if the queue is full and the oldest message
  96. * is discarded.
  97. */
  98. template <typename Ctx, typename MessageType, typename... Args>
  99. bool transmit(size_t queue_index, Args &&...args) {
  100. return send_impl<Ctx, MessageType, true>(queue_index,
  101. std::forward<Args>(args)...);
  102. }
  103. /** performs instant subscription to the specified message handler
  104. *
  105. * This method should be invoked from `initialize()`
  106. */
  107. template <typename Method> void subscribe(Method method) {
  108. assert(state == State::off);
  109. assert(backend_idx < (int)backends_count);
  110. auto ptr = backends_ptr + ++backend_idx * sizeof(Handler);
  111. make_handler(method, *reinterpret_cast<Handler *>(ptr));
  112. }
  113. protected:
  114. /** main actor ctor, takes pointer to backend storage and backends count.
  115. *
  116. * Sets the fail policy to escalate by default. */
  117. ActorBase(char *backends, size_t backends_count);
  118. /** this method is invoked, when actor received "change state" message
  119. * from it's supervisor, i.e. to advance from "off" state to "initialized"
  120. * state.
  121. *
  122. * The idea to override this method in your actor class to perform some
  123. * hardware initialization, may be asynchronously, and when it is ready
  124. * call the advance_init() in the parent class to acknowledge successful
  125. * initialization to supervisor.
  126. *
  127. * If the initialization fails, then instead of invoking the method,
  128. * the actor should send `message::ChangeStateAck` with the **false** value.
  129. *
  130. */
  131. virtual void advance_init();
  132. /** this method is invoked, when supervisor is ready to operate, i.e.
  133. * all other actors acknowledged successful initialialization
  134. * and it is time to initiate communication.
  135. *
  136. * Usually this is "pro-active" method, i.e. to send high-level
  137. * request to let all messaging machinery work
  138. */
  139. virtual void advance_start();
  140. /** this method is invoked when the actor is acked to shutdown by
  141. * supervisor.
  142. *
  143. * The idea is to override the method in a derived class, shutdown
  144. * hardware (possibly asynchronously), and then invoke the method
  145. * of the parent class.
  146. *
  147. * Contrary to the `advance_init` method, this method have to
  148. * be invoked sooner or later.
  149. */
  150. virtual void advance_stop();
  151. /** actor unique id, assigned once upon `bind` method */
  152. ActorId id;
  153. /** actor mask, used for messages routing.
  154. *
  155. * For simple actors (non-supervisors) it is equal to actor id
  156. *
  157. * For supervisors it is includes actor ids of all owned child-actors,
  158. * including masks of child-supervisors etc.
  159. */
  160. ActorId mask;
  161. /** current actor state */
  162. State state = State::off;
  163. /** pointer to the owner/supervisor */
  164. SupervisorBase *supervisor;
  165. /** actor's fail policy */
  166. FailPolicy fail_policy;
  167. private:
  168. template <typename Ctx, typename MessageType, bool force, typename... Args>
  169. bool send_impl(size_t queue_index, Args &&...args);
  170. void on_state_change(message::ChangeState &);
  171. template <typename Method>
  172. void make_handler(Method &method, Handler &handler) {
  173. using traits = handler_traits<Method>;
  174. using FinalMessage = typename traits::FinalMessage;
  175. using Backend = typename traits::Backend;
  176. handler.message_id = FinalMessage::type_id;
  177. handler.entry = [](Message &message, ActorBase &actor, Handler &handler) {
  178. auto &final_message = static_cast<FinalMessage &>(message);
  179. auto &backend = static_cast<Backend &>(actor);
  180. auto method = handler.method; // for proper alignment
  181. (backend.*reinterpret_cast<Method &>(method))(final_message);
  182. };
  183. handler.method = reinterpret_cast<GenericMethod &>(method);
  184. }
  185. char *backends_ptr;
  186. size_t backends_count;
  187. int backend_idx;
  188. friend struct SupervisorBase;
  189. };
  190. /** \struct Actor
  191. * \brief convenient templated base class for user-defined actors*/
  192. template <size_t HandlersCount> struct Actor : ActorBase {
  193. static_assert(HandlersCount >= Actor::min_handlers_amount,
  194. "no enough handlers");
  195. Actor() : ActorBase(reinterpret_cast<char *>(&backends), HandlersCount) {}
  196. /** storage of message handlers */
  197. ActorBase::Handler backends[HandlersCount];
  198. };
  199. } // namespace rotor_light