AWSApiRequestJob.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. #include <AzCore/Component/TickBus.h>
  10. #include <Framework/AWSApiClientJob.h>
  11. #include <Framework/AWSApiRequestJobConfig.h>
  12. namespace Aws
  13. {
  14. namespace Client
  15. {
  16. class AsyncCallerContext;
  17. }
  18. }
  19. namespace AWSCore
  20. {
  21. /// A macro that simplifies the declaration of AwsApiRequestTraits for APIs that have a result.
  22. ///
  23. /// \param SERVICE - the name of an Aws service. e.g. Lambda.
  24. ///
  25. /// \param REQUEST - the name of an Aws request supported by that
  26. /// service. e.g. Invoke.
  27. #define AWS_API_REQUEST_TRAITS(SERVICE, REQUEST) \
  28. AWSCore::AwsApiRequestTraits< \
  29. Aws::SERVICE::SERVICE##Client, \
  30. Aws::SERVICE::Model::REQUEST##Request, \
  31. Aws::SERVICE::Model::REQUEST##Outcome, \
  32. Aws::SERVICE::Model::REQUEST##OutcomeCallable, \
  33. Aws::SERVICE::REQUEST##ResponseReceivedHandler, \
  34. Aws::SERVICE::Model::REQUEST##Result, \
  35. Aws::Client::AWSError<Aws::SERVICE::SERVICE##Errors>, \
  36. &Aws::SERVICE::SERVICE##Client::REQUEST, \
  37. &Aws::SERVICE::SERVICE##Client::REQUEST##Callable, \
  38. &Aws::SERVICE::SERVICE##Client::REQUEST##Async \
  39. >
  40. /// A macro that simplifies the declaration of AwsApiRequestTraits for APIs that have no result.
  41. ///
  42. /// \param SERVICE - the name of an Aws service. e.g. Lambda.
  43. ///
  44. /// \param REQUEST - the name of an Aws request supported by that
  45. /// service. e.g. Invoke.
  46. #define AWS_API_REQUEST_TRAITS_NO_RESULT(SERVICE, REQUEST) \
  47. AWSCore::AwsApiRequestTraits< \
  48. Aws::SERVICE::SERVICE##Client, \
  49. Aws::SERVICE::Model::REQUEST##Request, \
  50. Aws::SERVICE::Model::REQUEST##Outcome, \
  51. Aws::SERVICE::Model::REQUEST##OutcomeCallable, \
  52. Aws::SERVICE::REQUEST##ResponseReceivedHandler, \
  53. Aws::NoResult, \
  54. Aws::Client::AWSError<Aws::SERVICE::SERVICE##Errors>, \
  55. &Aws::SERVICE::SERVICE##Client::REQUEST, \
  56. &Aws::SERVICE::SERVICE##Client::REQUEST##Callable, \
  57. &Aws::SERVICE::SERVICE##Client::REQUEST##Async \
  58. >
  59. /// Macro used below to define the traits type. Parameters
  60. /// correspond to those generated by the macro above.
  61. #define AWS_API_REQUEST_TRAITS_TEMPLATE_DEFINITION_HELPER \
  62. template< \
  63. class _ClientType, \
  64. class _RequestType, \
  65. class _OutcomeType, \
  66. class _OutcomeCallableType, \
  67. class _AsyncHandlerType, \
  68. class _ResultType, \
  69. class _ErrorType, \
  70. RequestFunctionType<_ClientType, _OutcomeType, _RequestType> _Function, \
  71. RequestCallableFunctionType<_ClientType, _OutcomeCallableType, _RequestType> _CallableFunction, \
  72. RequestAsyncFunctionType<_ClientType, _RequestType, _AsyncHandlerType> _AsyncFunction \
  73. >
  74. /// Macro used below to define the traits type. Parameters
  75. /// correspond to those generated by the macro above.
  76. #define AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER \
  77. AwsApiRequestTraits< \
  78. _ClientType, \
  79. _RequestType, \
  80. _OutcomeType, \
  81. _OutcomeCallableType, \
  82. _AsyncHandlerType, \
  83. _ResultType, \
  84. _ErrorType, \
  85. _Function, \
  86. _CallableFunction, \
  87. _AsyncFunction \
  88. >
  89. // const pointer to const sync execution member function of client type
  90. template<class ClientType, class OutcomeType, class RequestType>
  91. using RequestFunctionType = OutcomeType(ClientType::* const)(const RequestType&) const;
  92. // const pointer to const callable execution member function of client type
  93. template<class ClientType, class OutcomeCallableType, class RequestType>
  94. using RequestCallableFunctionType = OutcomeCallableType(ClientType::* const)(const RequestType&) const;
  95. // const pointer to const async execution member function of client type
  96. template<class ClientType, class RequestType, class AsyncHandlerType>
  97. using RequestAsyncFunctionType = void (ClientType::* const)(const RequestType&, const AsyncHandlerType&, const std::shared_ptr<const Aws::Client::AsyncCallerContext>&) const;
  98. /// Encapsulates everything we need to know to make an AWS service
  99. /// request of a particular type. Use the AWS_API_REQUEST_TRAITS macro
  100. /// instead of using this type directly.
  101. AWS_API_REQUEST_TRAITS_TEMPLATE_DEFINITION_HELPER
  102. struct AwsApiRequestTraits
  103. {
  104. using ClientType = _ClientType;
  105. using RequestType = _RequestType;
  106. using OutcomeType = _OutcomeType;
  107. using CallableOutcomeType = _OutcomeCallableType;
  108. using AsyncHandlerType = _AsyncHandlerType;
  109. using ResultType = _ResultType;
  110. using ErrorType = _ErrorType;
  111. using FunctionType = RequestFunctionType<_ClientType, _OutcomeType, _RequestType>;
  112. using CallableFunctionType = RequestCallableFunctionType<_ClientType, _OutcomeCallableType, _RequestType>;
  113. using AsyncFunctionType = RequestAsyncFunctionType<_ClientType, _RequestType, _AsyncHandlerType>;
  114. static FunctionType Function;
  115. static CallableFunctionType CallableFunction;
  116. static AsyncFunctionType AsyncFunction;
  117. };
  118. /// Define storage for AwsApiRequestTraits::Function
  119. AWS_API_REQUEST_TRAITS_TEMPLATE_DEFINITION_HELPER
  120. typename AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::FunctionType AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::Function = _Function;
  121. /// Define storage for AwsApiRequestTraits::CallableFunction
  122. AWS_API_REQUEST_TRAITS_TEMPLATE_DEFINITION_HELPER
  123. typename AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::CallableFunctionType AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::CallableFunction = _CallableFunction;
  124. /// Define storage for AwsApiRequestTraits::AsyncFunction
  125. AWS_API_REQUEST_TRAITS_TEMPLATE_DEFINITION_HELPER
  126. typename AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::AsyncFunctionType AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::AsyncFunction = _AsyncFunction;
  127. /// Macro that simplifies the declaration of an AwsRequestJob that has a result.
  128. #define AWS_API_REQUEST_JOB(SERVICE, REQUEST) AWSCore::AwsApiRequestJob<AWS_API_REQUEST_TRAITS(SERVICE, REQUEST)>
  129. /// Macro that simplifies the declaration of an AwsRequestJob that has no result.
  130. #define AWS_API_REQUEST_JOB_NO_RESULT(SERVICE, REQUEST) AWSCore::AwsApiRequestJob<AWS_API_REQUEST_TRAITS_NO_RESULT(SERVICE, REQUEST)>
  131. /// An Az::Job that that executes a specific AWS request.
  132. template<class RequestTraits>
  133. class AwsApiRequestJob
  134. : public AwsApiClientJob<typename RequestTraits::ClientType>
  135. {
  136. public:
  137. // To use a different allocator, extend this class and use this macro.
  138. AZ_CLASS_ALLOCATOR(AwsApiRequestJob, AZ::SystemAllocator);
  139. using AwsApiRequestJobType = AwsApiRequestJob<RequestTraits>;
  140. using AwsApiClientJobType = AwsApiClientJob<typename RequestTraits::ClientType>;
  141. using ClientType = typename RequestTraits::ClientType;
  142. using RequestType = typename RequestTraits::RequestType;
  143. using OutcomeType = typename RequestTraits::OutcomeType;
  144. using ResultType = typename RequestTraits::ResultType;
  145. using ErrorType = typename RequestTraits::ErrorType;
  146. using IConfig = IAwsApiRequestJobConfig<RequestTraits>;
  147. using Config = AwsApiRequestJobConfig<RequestTraits>;
  148. using OnSuccessFunction = AZStd::function<void(AwsApiRequestJob* job)>;
  149. using OnFailureFunction = AZStd::function<void(AwsApiRequestJob* job)>;
  150. class Function;
  151. template<class Allocator = AZ::SystemAllocator>
  152. static AwsApiRequestJob* Create(OnSuccessFunction onSuccess, OnFailureFunction onFailure = OnFailureFunction{}, IConfig* config = GetDefaultConfig());
  153. static Config* GetDefaultConfig()
  154. {
  155. static AwsApiJobConfigHolder<Config> s_configHolder{};
  156. return s_configHolder.GetConfig(AwsApiClientJobType::GetDefaultConfig());
  157. }
  158. AwsApiRequestJob(bool isAutoDelete, IConfig* config = GetDefaultConfig())
  159. : AwsApiClientJobType(isAutoDelete, config)
  160. {
  161. }
  162. /// Override AZ:Job defined method to reset request state when
  163. /// the job object is reused.
  164. void Reset(bool isClearDependent) override
  165. {
  166. request = RequestType{};
  167. result = ResultType{};
  168. error = ErrorType{};
  169. m_wasSuccess = false;
  170. AwsApiClientJobType::Reset(isClearDependent);
  171. }
  172. /// Determines if the request was successful.
  173. bool WasSuccess()
  174. {
  175. return m_wasSuccess;
  176. }
  177. RequestType request;
  178. ResultType result;
  179. ErrorType error;
  180. protected:
  181. void Process() override
  182. {
  183. // Currently the AWS SDK always executes requests synchronously. Even
  184. // when the Async and Callable versions of the API are used, the
  185. // actual AWS call is made synchronously, blocking whatever thread it
  186. // is executing on. Eventually the SDK may use true non-blocking
  187. // async I/O for requests. When that feature is available, we can
  188. // use the AZ::Job defined IncrementDependentCount method, start the
  189. // async i/o, and call WaitForChildren. When the i/o completes, we
  190. // would call DecrementDependentCount, which would cause WaitFor-
  191. // Children to return. We would then call OnOutcome.
  192. //
  193. // We want to call OnOutcome when the job is in a processing state
  194. // so that it can create and wait for child jobs, or do other
  195. // things that the job system requires it to be in the processing
  196. // state. That means that in the async case it has to be called
  197. // after the call to WaitForChildren returns (otherwise the job's
  198. // state is suspended).
  199. bool ok = PrepareRequest();
  200. if (ok)
  201. {
  202. // Get real pointer from shared pointer, then use with member
  203. // function pointer to call the function.
  204. OutcomeType outcome = (AwsApiClientJobType::m_client.get()->*RequestTraits::Function)(request);
  205. if (outcome.IsSuccess())
  206. {
  207. result = std::move(outcome.GetResultWithOwnership());
  208. m_wasSuccess = true;
  209. OnSuccess();
  210. }
  211. else
  212. {
  213. error = outcome.GetError();
  214. m_wasSuccess = false;
  215. OnFailure();
  216. }
  217. }
  218. }
  219. /// Called by Process to prepare the request. By default no changes
  220. /// are made to the request object. Override to defer the preparation
  221. /// of request data until your running on the job's worker thread,
  222. /// instead of setting the request data before calling Start.
  223. ///
  224. /// \return true if the request should be made.
  225. virtual bool PrepareRequest()
  226. {
  227. return true;
  228. }
  229. /// Called when request has completed successfully.
  230. virtual void OnSuccess()
  231. {
  232. }
  233. /// Called when the request fails.
  234. virtual void OnFailure()
  235. {
  236. }
  237. /// Called when request can't process and still requires cleanup (Specifically for the derived class AwsApiRequestJob<RequestTraits>::Function which does not use auto delete)
  238. virtual void DoCleanup()
  239. {
  240. }
  241. bool m_wasSuccess{ false };
  242. };
  243. /// A specialization of AwsApiRequestJob that lets you provide functions
  244. /// that are called on success or failure of the request.
  245. template<class RequestTraits>
  246. class AwsApiRequestJob<RequestTraits>::Function
  247. : public AwsApiRequestJob
  248. {
  249. public:
  250. // To use a different allocator, extend this class and use this macro.
  251. AZ_CLASS_ALLOCATOR(Function, AZ::SystemAllocator);
  252. Function(OnSuccessFunction onSuccess, OnFailureFunction onFailure = OnFailureFunction{}, IConfig* config = GetDefaultConfig())
  253. : AwsApiRequestJobType(
  254. false, config) // No auto delete - we need to perform our callbacks on the main thread so we queue them through tickbus
  255. , m_onSuccess{ onSuccess }
  256. , m_onFailure{ onFailure }
  257. {
  258. }
  259. private:
  260. void OnSuccess() override
  261. {
  262. AZStd::function<void()> callbackHandler = [this]()
  263. {
  264. if (m_onSuccess)
  265. {
  266. m_onSuccess(this);
  267. }
  268. delete this;
  269. };
  270. AZ::TickBus::QueueFunction(callbackHandler);
  271. }
  272. void OnFailure() override
  273. {
  274. AZStd::function<void()> callbackHandler = [this]()
  275. {
  276. if (m_onFailure)
  277. {
  278. m_onFailure(this);
  279. }
  280. delete this;
  281. };
  282. AZ::TickBus::QueueFunction(callbackHandler);
  283. }
  284. // Code doesn't use auto delete - this allows code to make sure things get cleaned up in cases where success or failure can't be
  285. // called.
  286. void DoCleanup() override
  287. {
  288. AZStd::function<void()> callbackHandler = [this]()
  289. {
  290. delete this;
  291. };
  292. AZ::TickBus::QueueFunction(callbackHandler);
  293. }
  294. OnSuccessFunction m_onSuccess;
  295. OnFailureFunction m_onFailure;
  296. };
  297. template<class RequestTraits>
  298. template<class Allocator>
  299. AwsApiRequestJob<RequestTraits>* AwsApiRequestJob<RequestTraits>::Create(
  300. OnSuccessFunction onSuccess, OnFailureFunction onFailure, IConfig* config)
  301. {
  302. return azcreate(Function, (onSuccess, onFailure, config), Allocator);
  303. }
  304. } // namespace AWSCore