actor_base.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. #pragma once
  2. //
  3. // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com)
  4. //
  5. // Distributed under the MIT Software License
  6. //
  7. #include "forward.hpp"
  8. #include "address.hpp"
  9. #include "actor_config.h"
  10. #include "messages.hpp"
  11. #include "state.h"
  12. #include "handler.hpp"
  13. #include "timer_handler.hpp"
  14. #include <set>
  15. namespace rotor {
  16. /** \struct actor_base_t
  17. * \brief universal primitive of concurrent computation
  18. *
  19. * The class is base class for user-defined actors. It is expected that
  20. * actors will react on incoming messages (e.g. by changing internal
  21. * /private state) or send (other) messages to other actors, or do
  22. * some side-effects (I/O, etc.).
  23. *
  24. * Message passing interface is asynchronous, they are send to {@link supervisor_t}.
  25. *
  26. * Every actor belong to some {@link supervisor_t}, which "injects" the thread-safe
  27. * execution context, in a sense, that the actor can call it's own methods as well
  28. * as supervirors without any need of synchonization.
  29. *
  30. * All actor methods are thread-unsafe, i.e. should not be called with except of
  31. * it's own supervisor. Communication with actor should be performed via messages.
  32. *
  33. * Actor is addressed by it's "main" address; however it is possible for an actor
  34. * to have multiple identities aka "virtual" addresses.
  35. *
  36. */
  37. struct actor_base_t : public arc_base_t<actor_base_t> {
  38. /** \brief injects an alias for actor_config_t */
  39. using config_t = actor_config_t;
  40. /** \brief injects templated actor_config_builder_t */
  41. template <typename Actor> using config_builder_t = actor_config_builder_t<Actor>;
  42. /** \brief SFINAE handler detector
  43. *
  44. * Either handler can be constructed from memeber-to-function-pointer or
  45. * it is already constructed and have a base `handler_base_t`
  46. */
  47. template <typename Handler>
  48. using is_handler =
  49. std::enable_if_t<std::is_member_function_pointer_v<Handler> || std::is_base_of_v<handler_base_t, Handler>>;
  50. // clang-format off
  51. /** \brief the default list of plugins for an actor
  52. *
  53. * The order of plugins is very important, as they are initialized in the direct order
  54. * and deinitilized in the reverse order.
  55. *
  56. */
  57. using plugins_list_t = std::tuple<
  58. plugin::address_maker_plugin_t,
  59. plugin::lifetime_plugin_t,
  60. plugin::init_shutdown_plugin_t,
  61. plugin::link_server_plugin_t,
  62. plugin::link_client_plugin_t,
  63. plugin::registry_plugin_t,
  64. plugin::resources_plugin_t,
  65. plugin::starter_plugin_t>;
  66. // clang-format on
  67. /** \brief constructs actor and links it's supervisor
  68. *
  69. * An actor cannot outlive it's supervisor.
  70. *
  71. * Sets internal actor state to `NEW`
  72. *
  73. */
  74. actor_base_t(config_t &cfg);
  75. virtual ~actor_base_t();
  76. /** \brief early actor initialization (pre-initialization)
  77. *
  78. * Actor's plugins are activated, "main" address is created
  79. * (via {@link plugin::address_maker_plugin_t}), state is set
  80. * to `INITIALIZING` (via {@link plugin::init_shutdown_plugin_t}).
  81. *
  82. */
  83. virtual void do_initialize(system_context_t *ctx) noexcept;
  84. /** \brief convenient method to send actor's supervisor shutdown trigger message */
  85. virtual void do_shutdown() noexcept;
  86. /** \brief actor is fully initialized and it's supervisor has sent signal to start
  87. *
  88. * The actor state is set to `OPERATIONAL`.
  89. *
  90. */
  91. virtual void on_start() noexcept;
  92. /** \brief sends message to the destination address
  93. *
  94. * Internally it just constructs new message in supervisor's outbound queue.
  95. *
  96. */
  97. template <typename M, typename... Args> void send(const address_ptr_t &addr, Args &&... args);
  98. /** \brief returns request builder for destination address using the "main" actor address
  99. *
  100. * The `args` are forwarded for construction of the request. The request is not actually sent,
  101. * until `send` method of {@link request_builder_t} will be invoked.
  102. *
  103. * Supervisor will spawn timeout timer upon `timeout` method.
  104. */
  105. template <typename R, typename... Args>
  106. request_builder_t<typename request_wrapper_t<R>::request_t> request(const address_ptr_t &dest_addr,
  107. Args &&... args);
  108. /** \brief returns request builder for destination address using the specified address for reply
  109. *
  110. * It is assumed, that the specified address belongs to the actor.
  111. *
  112. * The method is useful, when a different behavior is needed for the same
  113. * message response types. It serves at some extend as virtual dispatching within
  114. * the actor.
  115. *
  116. * See the description of `request` method.
  117. *
  118. */
  119. template <typename R, typename... Args>
  120. request_builder_t<typename request_wrapper_t<R>::request_t>
  121. request_via(const address_ptr_t &dest_addr, const address_ptr_t &reply_addr, Args &&... args);
  122. /** \brief convenient method for constructing and sending response to a request
  123. *
  124. * `args` are forwarded to response payload constuction
  125. */
  126. template <typename Request, typename... Args> void reply_to(Request &message, Args &&... args);
  127. /** \brief convenient method for constructing and sending error response to a request */
  128. template <typename Request> void reply_with_error(Request &message, const std::error_code &ec);
  129. /** \brief makes response to the request, but does not send it.
  130. *
  131. * The return type is intrusive pointer to the message, not the message itself.
  132. *
  133. * It can be useful for delayed responses. The response can be dispatched later via
  134. * supervisor->put(std::move(response_ptr));
  135. *
  136. */
  137. template <typename Request, typename... Args> auto make_response(Request &message, Args &&... args);
  138. /** \brief makes error response to the request, but does not send it.
  139. *
  140. * The return type is intrusive pointer to the message, not the message itself.
  141. *
  142. * It can be useful for delayed responses. The response can be dispatched later via
  143. * supervisor->put(std::move(response_ptr));
  144. *
  145. */
  146. template <typename Request> auto make_response(Request &message, const std::error_code &ec);
  147. /** \brief subscribes actor's handler to process messages on the specified address */
  148. template <typename Handler> subscription_info_ptr_t subscribe(Handler &&h, const address_ptr_t &addr) noexcept;
  149. /** \brief subscribes actor's handler to process messages on the actor's "main" address */
  150. template <typename Handler> subscription_info_ptr_t subscribe(Handler &&h) noexcept;
  151. /** \brief unsubscribes actor's handler from process messages on the specified address */
  152. template <typename Handler, typename = is_handler<Handler>>
  153. void unsubscribe(Handler &&h, address_ptr_t &addr) noexcept;
  154. /** \brief unsubscribes actor's handler from processing messages on the actor's "main" address */
  155. template <typename Handler, typename = is_handler<Handler>> void unsubscribe(Handler &&h) noexcept;
  156. /* \brief initiates handler unsubscription from the address
  157. *
  158. * If the address is local, then unsubscription confirmation is sent immediately,
  159. * otherwise {@link payload::external_subscription_t} request is sent to the external
  160. * supervisor, which owns the address.
  161. *
  162. * The optional call can be providded to be called upon message destruction.
  163. *
  164. */
  165. /** \brief initiates handler unsubscription from the default actor address */
  166. inline void unsubscribe(const handler_ptr_t &h) noexcept { lifetime->unsubscribe(h, address); }
  167. /** \brief starts plugins activation */
  168. void activate_plugins() noexcept;
  169. /** \brief finishes plugin activation, successful or not */
  170. void commit_plugin_activation(plugin::plugin_base_t &plugin, bool success) noexcept;
  171. /** \brief starts plugins deactivation */
  172. void deactivate_plugins() noexcept;
  173. /** \brief finishes plugin deactivation */
  174. void commit_plugin_deactivation(plugin::plugin_base_t &plugin) noexcept;
  175. /** \brief propaagtes subscription message to corresponding actors */
  176. void on_subscription(message::subscription_t &message) noexcept;
  177. /** \brief propaagtes unsubscription message to corresponding actors */
  178. void on_unsubscription(message::unsubscription_t &message) noexcept;
  179. /** \brief propaagtes external unsubscription message to corresponding actors */
  180. void on_unsubscription_external(message::unsubscription_external_t &message) noexcept;
  181. /** \brief creates new unique address for an actor (via address_maker plugin) */
  182. address_ptr_t create_address() noexcept;
  183. /** \brief starts shutdown procedure, e.g. upon receiving shutdown request
  184. *
  185. * The actor state is set to SHUTTING_DOWN.
  186. *
  187. */
  188. virtual void shutdown_start() noexcept;
  189. /** \brief polls plugins for shutdown
  190. *
  191. * The poll is performed in the reverse order. If all plugins, with active
  192. * shutdown reaction confirm they are ready to shutdown, then the
  193. * `shutdown_finish` method is invoked.
  194. *
  195. */
  196. void shutdown_continue() noexcept;
  197. /** \brief finalizes shutdown
  198. *
  199. * The shutdown response is sent and actor state is set to SHUT_DOWN.
  200. *
  201. * This is the last action in the shutdown sequence.
  202. * No further methods will be invoked on the actor
  203. *
  204. */
  205. virtual void shutdown_finish() noexcept;
  206. /** \brief starts initialization procedure
  207. *
  208. * The actor state is set to INITIALIZING.
  209. *
  210. */
  211. virtual void init_start() noexcept;
  212. /** \brief polls plugins whether they completed initialization.
  213. *
  214. * The poll is performed in the direct order. If all plugins, with active
  215. * init reaction confirm they are ready, then the `init_finish` method
  216. * is invoked.
  217. *
  218. */
  219. void init_continue() noexcept;
  220. /** \brief finalizes initialization
  221. *
  222. * The init response is sent and actor state is set to INITIALIZED.
  223. *
  224. */
  225. virtual void init_finish() noexcept;
  226. /** \brief main callback for plugin configuration when it's ready */
  227. virtual void configure(plugin::plugin_base_t &plugin) noexcept;
  228. /** \brief generic non-public fields accessor */
  229. template <typename T> auto &access() noexcept;
  230. /** \brief generic non-public methods accessor */
  231. template <typename T, typename... Args> auto access(Args... args) noexcept;
  232. /** \brief generic non-public fields accessor */
  233. template <typename T> auto &access() const noexcept;
  234. /** \brief generic non-public methods accessor */
  235. template <typename T, typename... Args> auto access(Args... args) const noexcept;
  236. /** \brief returns actor's main address */
  237. inline const address_ptr_t &get_address() const noexcept { return address; }
  238. /** \brief returns actor's supervisor */
  239. inline supervisor_t &get_supervisor() noexcept { return *supervisor; }
  240. /** \brief spawns a new one-shot timer
  241. *
  242. * \param interval specifies amount of time, after which the timer will trigger.
  243. * \param delegate is an object of arbitrary class.
  244. * \param method is the pointer-to-member-function of the object, which will be
  245. * invoked upon timer triggering or cancellation.
  246. *
  247. * The `method` parameter should have the following signature:
  248. *
  249. * void Delegate::on_timer(request_id_t, bool cancelled) noexcept;
  250. *
  251. * `start_timer` returns timer identity. It will be supplied to the specified callback,
  252. * or the timer can be cancelled via it.
  253. */
  254. template <typename Delegate, typename Method>
  255. request_id_t start_timer(const pt::time_duration &interval, Delegate &delegate, Method method) noexcept;
  256. /** \brief cancels previously started timer
  257. *
  258. * If timer hasn't been triggered, then it is cancelled and the callback will be invoked
  259. * with `true` to mark that it was cancelled.
  260. */
  261. void cancel_timer(request_id_t request_id) noexcept;
  262. protected:
  263. /** \brief timer-id to timer-handler map (type) */
  264. using timers_map_t = std::unordered_map<request_id_t, timer_handler_ptr_t>;
  265. /** \brief triggers timer handler associated with the timer id */
  266. void on_timer_trigger(request_id_t request_id, bool cancelled) noexcept;
  267. /** \brief starts timer with pre-forged timer id (aka request-id */
  268. template <typename Delegate, typename Method>
  269. void start_timer(request_id_t request_id, const pt::time_duration &interval, Delegate &delegate,
  270. Method method) noexcept;
  271. /** \brief suspended init request message */
  272. intrusive_ptr_t<message::init_request_t> init_request;
  273. /** \brief suspended shutdown request message */
  274. intrusive_ptr_t<message::shutdown_request_t> shutdown_request;
  275. /** \brief actor address */
  276. address_ptr_t address;
  277. /** \brief non-owning pointer to actor's execution / infrastructure context */
  278. supervisor_t *supervisor;
  279. /** \brief opaque plugins storage (owning) */
  280. plugin_storage_ptr_t plugins_storage;
  281. /** \brief non-ownling list of plugins */
  282. plugins_t plugins;
  283. /** \brief timeout for actor initialization (used by supervisor) */
  284. pt::time_duration init_timeout;
  285. /** \brief timeout for actor shutdown (used by supervisorr) */
  286. pt::time_duration shutdown_timeout;
  287. /** \brief current actor state */
  288. state_t state;
  289. /** \brief non-owning pointer to address_maker plugin */
  290. plugin::address_maker_plugin_t *address_maker = nullptr;
  291. /** \brief non-owning pointer to lifetime plugin */
  292. plugin::lifetime_plugin_t *lifetime = nullptr;
  293. /** \brief non-owning pointer to link_server plugin */
  294. plugin::link_server_plugin_t *link_server = nullptr;
  295. /** \brief non-owning pointer to resources plugin */
  296. plugin::resources_plugin_t *resources = nullptr;
  297. /** \brief finds plugin by plugin class identity
  298. *
  299. * `nullptr` is returned when plugin cannot be found
  300. */
  301. plugin::plugin_base_t *get_plugin(const void *identity) const noexcept;
  302. /** \brief set of activating plugin identities */
  303. std::set<const void *> activating_plugins;
  304. /** \brief set of deactivating plugin identities */
  305. std::set<const void *> deactivating_plugins;
  306. /** \brief timer-id to timer-handler map */
  307. timers_map_t timers_map;
  308. friend struct plugin::plugin_base_t;
  309. friend struct plugin::lifetime_plugin_t;
  310. friend struct supervisor_t;
  311. template <typename T> friend struct request_builder_t;
  312. template <typename T, typename M> friend struct accessor_t;
  313. };
  314. } // namespace rotor