request.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. #pragma once
  2. //
  3. // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com)
  4. //
  5. // Distributed under the MIT Software License
  6. //
  7. #include "address.hpp"
  8. #include "message.h"
  9. #include "extended_error.h"
  10. #include "forward.hpp"
  11. #include <unordered_map>
  12. namespace rotor {
  13. /** \struct request_base_t
  14. * \brief base class for request payload
  15. */
  16. struct request_base_t {
  17. /** \brief unique (per supervisor) request id */
  18. request_id_t id;
  19. /** \brief destination address for reply
  20. *
  21. * It is not necessary that the address be the original actor
  22. *
  23. */
  24. address_ptr_t reply_to;
  25. /** \brief the source (original) actor address, which made an request */
  26. address_ptr_t origin;
  27. };
  28. /** \brief optionally wraps request type into intrusive pointer
  29. *
  30. * The transformation (`T -> iptr<T>`) occurs only if the type
  31. * `T` is descendant of `arc_base_t<T>`.
  32. *
  33. * Otherwise, the user supplied request type `T` remains the same.
  34. *
  35. */
  36. template <typename T, typename = void> struct request_wrapper_t {
  37. /** \brief an alias for the original request type */
  38. using request_t = T;
  39. };
  40. /** \brief request_wrapper_t specialization for refcounted item */
  41. template <typename T> struct request_wrapper_t<T, std::enable_if_t<std::is_base_of_v<arc_base_t<T>, T>>> {
  42. /** \brief an alias for the original request type, wrapped into intrusive pointer */
  43. using request_t = intrusive_ptr_t<T>;
  44. };
  45. /** \brief optionally unwraps request type from intrusive pointer
  46. *
  47. * The transformation (`iptr<T> -> T`) occurs only if the type
  48. * `T` already wraps T into intrusive pointer
  49. *
  50. * Otherwise, the user supplied request type `T` remains the same.
  51. *
  52. */
  53. template <typename T, typename = void> struct request_unwrapper_t {
  54. /** \brief an alias for the original request type */
  55. using request_t = T;
  56. };
  57. /** \brief request_unwrapper_t specialization for intrusive pointer of T */
  58. template <typename T> struct request_unwrapper_t<intrusive_ptr_t<T>> {
  59. /** \brief an alias for the original request type, if it was wrapped into intrusive poitner */
  60. using request_t = T;
  61. };
  62. /** \struct wrapped_request_t
  63. * \brief templated request, which is able to hold user-supplied payload
  64. *
  65. * It contains as user-supplied payload as well as rotor-specific
  66. * information for request tracking (request_id, reply address).
  67. *
  68. */
  69. template <typename T, typename = void> struct wrapped_request_t : request_base_t {
  70. /** \brief alias for original (unwrapped) request payload type */
  71. using request_t = T;
  72. /** \brief alias for original (unwrapped) response payload type */
  73. using response_t = typename T::response_t;
  74. /** \brief constructs wrapper for user-supplied payload from request-id and
  75. * and destination reply address */
  76. template <typename... Args>
  77. wrapped_request_t(request_id_t id_, const address_ptr_t &reply_to_, const address_ptr_t &origin_, Args &&...args)
  78. : request_base_t{id_, reply_to_, origin_}, request_payload{std::forward<Args>(args)...} {}
  79. /** \brief original, user-supplied payload */
  80. T request_payload;
  81. };
  82. /** \struct cancellation_t
  83. * \brief Request cancellation payload
  84. *
  85. */
  86. template <typename T> struct cancellation_t {
  87. /** \brief unique (per supervisor) request id */
  88. request_id_t id;
  89. /** \brief actor address, which initiated the request */
  90. address_ptr_t source;
  91. };
  92. /**
  93. * \brief wrapped request specialization, when the request should be wrapped into intrusive pointer
  94. */
  95. template <typename T>
  96. struct wrapped_request_t<T, std::enable_if_t<std::is_base_of_v<arc_base_t<T>, T>>> : request_base_t {
  97. /** \brief alias for original (unwrapped) request payload type */
  98. using raw_request_t = T;
  99. /** \brief alias for original request payload type wrapped into intrusive pointer */
  100. using request_t = intrusive_ptr_t<T>;
  101. /** \brief alias for original (unwrapped) response payload type */
  102. using response_t = typename T::response_t;
  103. /** \brief makes an intrusive pointer for already constructed user-supplied payload
  104. *
  105. * The different `request-id` and `reply_to` address arguments are supplied
  106. * to make it possible cheaply forward requests.
  107. */
  108. wrapped_request_t(request_id_t id_, const address_ptr_t &reply_to_, const address_ptr_t &origin_,
  109. const request_t &request_)
  110. : request_base_t{id_, reply_to_, origin_}, request_payload{request_} {}
  111. /** \brief constructs wrapper for user-supplied payload from request-id and
  112. * and destination reply address */
  113. template <typename... Args, typename E = std::enable_if_t<std::is_constructible_v<raw_request_t, Args...>>>
  114. wrapped_request_t(request_id_t id_, const address_ptr_t &reply_to_, const address_ptr_t &origin_, Args &&...args)
  115. : request_base_t{id_, reply_to_, origin_}, request_payload{new raw_request_t{std::forward<Args>(args)...}} {}
  116. /** \brief intrusive pointer to user-supplied payload */
  117. request_t request_payload;
  118. };
  119. /** \struct response_helper_t
  120. * \brief generic helper, which helps to construct user-defined response payload
  121. */
  122. template <typename Response> struct response_helper_t {
  123. /** original user-supplied response type */
  124. using response_t = Response;
  125. /** \brief constructs user defined response payload */
  126. template <typename... Args> static Response construct(Args &&...args) {
  127. return Response{std::forward<Args>(args)...};
  128. }
  129. };
  130. /**
  131. * \brief specific helper, which helps to construct intrusive pointer to user-defined
  132. * response payload
  133. */
  134. template <typename Response> struct response_helper_t<intrusive_ptr_t<Response>> {
  135. /** \brief type for intrusive pointer user defined response payload */
  136. using res_ptr_t = intrusive_ptr_t<Response>;
  137. /** original user-supplied response type */
  138. using response_t = Response;
  139. /** \brief constructs intrusive pointer to user defined response payload */
  140. template <typename... Args> static res_ptr_t construct(Args &&...args) {
  141. return res_ptr_t{new Response{std::forward<Args>(args)...}};
  142. }
  143. /** \brief constructs user defined response payload and wraps it into intrusive pointer */
  144. template <typename T, typename std::enable_if_t<std::is_same_v<T, res_ptr_t>>> static res_ptr_t construct(T &&ptr) {
  145. return res_ptr_t{std::forward<T>(ptr)};
  146. }
  147. };
  148. namespace details {
  149. template <class T, typename... Args> decltype(void(T{std::declval<Args>()...}), std::true_type()) test(int);
  150. template <class T, typename... Args> std::false_type test(...);
  151. template <class T, typename... Args> struct is_braces_constructible : decltype(test<T, Args...>(0)) {};
  152. template <class T, class... Args> constexpr auto is_braces_constructible_v = is_braces_constructible<T, Args...>::value;
  153. template <typename... Ts> struct size_of_t;
  154. template <typename T> struct size_of_t<T> : std::is_default_constructible<T> {};
  155. template <typename T, typename... Ts> struct size_of_t<T, Ts...> : public std::is_constructible<T, Ts...> {};
  156. template <typename T, typename... Args>
  157. inline constexpr bool is_somehow_constructible_v =
  158. std::is_constructible_v<T, Args...> || is_braces_constructible_v<T, Args...>;
  159. /** \brief main helper to check constructability of T from Args... */
  160. template <typename T, typename E = void, typename... Args>
  161. struct is_constructible : is_constructible<T, void, E, Args...> {};
  162. /** \brief checks whether T is default-constructible */
  163. template <typename T> struct is_constructible<T, void> {
  164. /** \brief returns true fi type T is default-constructible */
  165. static constexpr auto value = std::is_default_constructible_v<T>;
  166. };
  167. /** \brief checks whether it is possible construct T from Arg */
  168. template <typename T, typename Arg> struct is_constructible<T, Arg> {
  169. /** \brief returns true fi type T is constructible or braces-constructible from `Arg` */
  170. static constexpr auto value = is_somehow_constructible_v<T, Arg>;
  171. };
  172. /** \brief checks whether it is possible construct T from Arg */
  173. template <typename T, typename Arg> struct is_constructible<T, void, Arg> {
  174. /** \brief returns true fi type T is constructible or braces-constructible from `Arg` */
  175. static constexpr auto value = is_somehow_constructible_v<T, Arg>;
  176. };
  177. /** \brief checks whether it is possible construct T from Args... */
  178. template <typename T, typename... Args> struct is_constructible<T, void, Args...> {
  179. /** \brief returns true fi type T is constructible or braces-constructible from `Args...` */
  180. static constexpr auto value = is_somehow_constructible_v<T, Args...>;
  181. };
  182. template <typename T, typename... Args> inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;
  183. } // namespace details
  184. /** \struct wrapped_response_t
  185. * \brief trackable templated response which holds user-supplied response payload.
  186. *
  187. * In addition to user-supplied response payload, the class contains `error_code`
  188. * and intrusive pointer to the original request message.
  189. *
  190. */
  191. template <typename Request> struct wrapped_response_t {
  192. /** \brief alias for original user-supplied request type */
  193. using request_t = typename request_unwrapper_t<Request>::request_t;
  194. /** \brief alias type of message with wrapped request, which is possibly wrapped into intrusive pointer */
  195. using req_message_t = message_t<wrapped_request_t<request_t>>;
  196. /** \brief alias for intrusive pointer to message with wrapped request */
  197. using req_message_ptr_t = intrusive_ptr_t<req_message_t>;
  198. /** \brief alias for possibly wrapped user-supplied response type */
  199. using response_t = typename request_t::response_t;
  200. /** \brief helper type for response construction */
  201. using res_helper_t = response_helper_t<response_t>;
  202. /** \brief alias user-supplied response type */
  203. using unwrapped_response_t = typename res_helper_t::response_t;
  204. static_assert(std::is_default_constructible_v<response_t>, "response type must be default-constructible");
  205. /** \brief pointer to extended error, used in the case of response failure */
  206. extended_error_ptr_t ee;
  207. /** \brief original request message, which contains request_id for request/response matching */
  208. req_message_ptr_t req;
  209. /** \brief user-supplied response payload */
  210. response_t res;
  211. /** \brief error-response constructor (response payload is empty) */
  212. wrapped_response_t(const extended_error_ptr_t &ee_, req_message_ptr_t message_)
  213. : ee{ee_}, req{std::move(message_)} {}
  214. /** \brief "forward-constructor"
  215. *
  216. * The request message, error code are copied, while the response (possible intrusive
  217. * pointer to the original request) is forwarded.
  218. *
  219. */
  220. template <typename Response, typename E = std::enable_if_t<std::is_same_v<response_t, std::remove_cv_t<Response>>>>
  221. wrapped_response_t(req_message_ptr_t message_, const extended_error_ptr_t &ee_, Response &&res_)
  222. : ee{ee_}, req{std::move(message_)}, res{std::forward<Response>(res_)} {}
  223. /** \brief successful-response constructor.
  224. *
  225. * The request message is copied, the error code is set to success,
  226. * the response (possible intrusive pointer to the original request) constructed from
  227. * the arguments.
  228. *
  229. */
  230. template <typename Req, typename... Args,
  231. typename E1 = std::enable_if_t<std::is_same_v<req_message_ptr_t, std::remove_cv_t<Req>>>,
  232. typename E2 = std::enable_if_t<details::is_constructible_v<unwrapped_response_t, Args...>>>
  233. wrapped_response_t(Req &&message_, Args &&...args)
  234. : ee{}, req{std::forward<Req>(message_)}, res{res_helper_t::construct(std::forward<Args>(args)...)} {}
  235. /** \brief returns request id of the original request */
  236. inline request_id_t request_id() const noexcept { return req->payload.id; }
  237. };
  238. /** \brief free function type, which produces error response to the original request */
  239. typedef message_ptr_t(error_producer_t)(const address_ptr_t &reply_to, message_base_t &msg,
  240. const extended_error_ptr_t &ec) noexcept;
  241. /** \struct request_curry_t
  242. * \brief the recorded context, which is needed to produce error response to the original request */
  243. struct request_curry_t {
  244. /** \brief the free function, which produces error response */
  245. error_producer_t *fn;
  246. /** \brief destination address for the error response */
  247. address_ptr_t origin;
  248. /** \brief the original request message */
  249. message_ptr_t request_message;
  250. /** \brief actor, on which behalf the original request has been made */
  251. actor_base_t *source;
  252. };
  253. /** \struct request_traits_t
  254. * \brief type helper to deduce request/response messages from original (user-supplied) request type
  255. */
  256. template <typename R> struct request_traits_t {
  257. /** \brief alias for original request payload type */
  258. using request_t = typename request_unwrapper_t<R>::request_t;
  259. /** \struct request
  260. * \brief request related types */
  261. struct request {
  262. /** \brief alias for possibly wrapped (to intrusive pointer) request payload type */
  263. using type = request_t;
  264. /** \brief wrapped (trackable) request payload */
  265. using wrapped_t = wrapped_request_t<type>;
  266. /** \brief message type with with wrapped request payload */
  267. using message_t = rotor::message_t<wrapped_t>;
  268. /** \brief intrusive pointer type for request message */
  269. using message_ptr_t = intrusive_ptr_t<message_t>;
  270. /** \brief alias for concrete message type visitor */
  271. using visitor_t = typename message_t::visitor_t;
  272. };
  273. /** \struct response
  274. * \brief response related types */
  275. struct response {
  276. /** \brief wrapped response payload (contains original request message) */
  277. using wrapped_t = wrapped_response_t<request_t>;
  278. /** \brief message type for wrapped response */
  279. using message_t = rotor::message_t<wrapped_t>;
  280. /** \brief intrusive pointer type for response message */
  281. using message_ptr_t = intrusive_ptr_t<message_t>;
  282. /** \brief alias for concrete message type visitor */
  283. using visitor_t = typename message_t::visitor_t;
  284. };
  285. /** \struct cancel
  286. * \brief cancel request related types */
  287. struct cancel {
  288. /** \brief the payload needed to cancel a request */
  289. using cancel_payload_t = cancellation_t<request_t>;
  290. /** \brief request cancellation message */
  291. using message_t = rotor::message_t<cancel_payload_t>;
  292. /** \brief alias for concrete message type visitor */
  293. using visitor_t = typename message_t::visitor_t;
  294. };
  295. /** \brief helper free function to produce error reply to the original request */
  296. static message_ptr_t make_error_response(const address_ptr_t &reply_to, message_base_t &message,
  297. const extended_error_ptr_t &ee) noexcept {
  298. using reply_message_t = typename response::message_t;
  299. using request_message_ptr = typename request::message_ptr_t;
  300. auto &request = static_cast<typename request::message_t &>(message);
  301. auto req_ptr = request_message_ptr(&request);
  302. auto raw_reply = new reply_message_t{reply_to, ee, req_ptr};
  303. return message_ptr_t{raw_reply};
  304. }
  305. };
  306. /** \struct request_builder_t
  307. * \brief builder pattern implementation for the original request
  308. */
  309. template <typename T> struct [[nodiscard]] request_builder_t {
  310. /** \brief constructs request message but still does not dispatch it */
  311. template <typename... Args>
  312. request_builder_t(supervisor_t &sup_, actor_base_t &actor_, const address_ptr_t &destination_,
  313. const address_ptr_t &reply_to_, Args &&...args);
  314. /** \brief actually dispatches requests and spawns timeout timer
  315. *
  316. * The request id of the dispatched request is returned
  317. *
  318. */
  319. request_id_t send(const pt::time_duration &send) noexcept;
  320. private:
  321. using traits_t = request_traits_t<T>;
  322. using request_message_t = typename traits_t::request::message_t;
  323. using request_message_ptr_t = typename traits_t::request::message_ptr_t;
  324. using response_message_t = typename traits_t::response::message_t;
  325. using response_message_ptr_t = typename traits_t::response::message_ptr_t;
  326. using wrapped_res_t = typename traits_t::response::wrapped_t;
  327. supervisor_t &sup;
  328. actor_base_t &actor;
  329. request_id_t request_id;
  330. const address_ptr_t &destination;
  331. const address_ptr_t &reply_to;
  332. bool do_install_handler;
  333. request_message_ptr_t req;
  334. address_ptr_t imaginary_address;
  335. void install_handler() noexcept;
  336. };
  337. } // namespace rotor