actor.hpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // SPDX-License-Identifier: MIT
  2. // SPDX-FileCopyrightText: 2022 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. /// holds message_id to do runtime message matching
  35. MessageTypeId message_id;
  36. /// Method entry item
  37. MethodEntry entry;
  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 emplaces 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. /** performs instant subscription to the specified message handler
  86. *
  87. * This method should be invoked from `initialize()`
  88. */
  89. template <typename Method> void subscribe(Method method) {
  90. assert(state == State::off);
  91. assert(backend_idx < (int)backends_count);
  92. auto ptr = backends_ptr + ++backend_idx * sizeof(Handler);
  93. make_handler(method, *reinterpret_cast<Handler *>(ptr));
  94. }
  95. protected:
  96. /** main actor ctor, takes pointer to backend storage and backends count.
  97. *
  98. * Sets the fail policy to escalate by default. */
  99. ActorBase(char *backends, size_t backends_count);
  100. /** this method is invoked, when actor received "change state" message
  101. * from it's supervisor, i.e. to advance from "off" state to "initialized"
  102. * state.
  103. *
  104. * The idea to override this method in your actor class to perform some
  105. * hardware initialization, may be asynchronously, and when it is ready
  106. * call the advance_init() in the parent class to acknowledge successful
  107. * initialization to supervisor.
  108. *
  109. * If the initialization fails, then instead of invoking the method,
  110. * the actor should send `message::ChangeStateAck` with the **false** value.
  111. *
  112. */
  113. virtual void advance_init();
  114. /** this method is invoked, when supervisor is ready to operate, i.e.
  115. * all other actors acknowledged successful initialialization
  116. * and it is time to initiate communication.
  117. *
  118. * Usually this is "pro-active" method, i.e. to send high-level
  119. * request to let all messaging machinery work
  120. */
  121. virtual void advance_start();
  122. /** this method is invoked when the actor is acked to shutdown by
  123. * supervisor.
  124. *
  125. * The idea is to override the method in a derived class, shutdown
  126. * hardware (possibly asynchronously), and then invoke the method
  127. * of the parent class.
  128. *
  129. * Contrary to the `advance_init` method, this method have to
  130. * be invoked sooner or later.
  131. */
  132. virtual void advance_stop();
  133. /** actor unique id, assigned once upon `bind` method */
  134. ActorId id;
  135. /** actor mask, used for messages routing.
  136. *
  137. * For simple actors (non-supervisors) it is equal to actor id
  138. *
  139. * For supervisors it is includes actor ids of all owned child-actors,
  140. * including masks of child-supervisors etc.
  141. */
  142. ActorId mask;
  143. /** current actor state */
  144. State state = State::off;
  145. /** pointer to the owner/supervisor */
  146. SupervisorBase *supervisor;
  147. /** actor's fail policy */
  148. FailPolicy fail_policy;
  149. private:
  150. void on_state_change(message::ChangeState &);
  151. template <typename Method>
  152. void make_handler(Method &method, Handler &handler) {
  153. using traits = handler_traits<Method>;
  154. using FinalMessage = typename traits::FinalMessage;
  155. using Backend = typename traits::Backend;
  156. handler.message_id = FinalMessage::type_id;
  157. handler.entry = [](Message &message, ActorBase &actor, Handler &handler) {
  158. auto &final_message = static_cast<FinalMessage &>(message);
  159. auto &backend = static_cast<Backend &>(actor);
  160. (backend.*reinterpret_cast<Method &>(handler.method))(final_message);
  161. };
  162. handler.method = reinterpret_cast<GenericMethod &>(method);
  163. }
  164. char *backends_ptr;
  165. size_t backends_count;
  166. int backend_idx;
  167. friend struct SupervisorBase;
  168. };
  169. /** \struct Actor
  170. * \brief convenient templated base class for user-defined actors*/
  171. template <size_t HandlersCount> struct Actor : ActorBase {
  172. static_assert(HandlersCount >= Actor::min_handlers_amount,
  173. "no enough handlers");
  174. Actor() : ActorBase(reinterpret_cast<char *>(&backends), HandlersCount) {}
  175. /** storage of message handlers */
  176. ActorBase::Handler backends[HandlersCount];
  177. };
  178. } // namespace rotor_light