post_async_results.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef BASE_WIN_POST_ASYNC_RESULTS_H_
  5. #define BASE_WIN_POST_ASYNC_RESULTS_H_
  6. #include <unknwn.h>
  7. #include <windows.foundation.h>
  8. #include <wrl/async.h>
  9. #include <wrl/client.h>
  10. #include <type_traits>
  11. #include <utility>
  12. #include "base/bind.h"
  13. #include "base/callback.h"
  14. #include "base/location.h"
  15. #include "base/logging.h"
  16. #include "base/threading/sequenced_task_runner_handle.h"
  17. namespace base {
  18. namespace win {
  19. namespace internal {
  20. // Utility function to pretty print enum values.
  21. constexpr const char* ToCString(AsyncStatus async_status) {
  22. switch (async_status) {
  23. case AsyncStatus::Started:
  24. return "AsyncStatus::Started";
  25. case AsyncStatus::Completed:
  26. return "AsyncStatus::Completed";
  27. case AsyncStatus::Canceled:
  28. return "AsyncStatus::Canceled";
  29. case AsyncStatus::Error:
  30. return "AsyncStatus::Error";
  31. }
  32. NOTREACHED();
  33. return "";
  34. }
  35. template <typename T>
  36. using IAsyncOperationT = typename ABI::Windows::Foundation::IAsyncOperation<T>;
  37. template <typename T>
  38. using IAsyncOperationCompletedHandlerT =
  39. typename base::OnceCallback<void(IAsyncOperationT<T>*, AsyncStatus)>;
  40. template <typename T>
  41. using AsyncAbiT = typename ABI::Windows::Foundation::Internal::GetAbiType<
  42. typename IAsyncOperationT<T>::TResult_complex>::type;
  43. // Compile time switch to decide what container to use for the async results for
  44. // |T|. Depends on whether the underlying Abi type is a pointer to IUnknown or
  45. // not. It queries the internals of Windows::Foundation to obtain this
  46. // information.
  47. template <typename T>
  48. using AsyncResultsT = std::conditional_t<
  49. std::is_convertible<AsyncAbiT<T>, IUnknown*>::value,
  50. Microsoft::WRL::ComPtr<std::remove_pointer_t<AsyncAbiT<T>>>,
  51. AsyncAbiT<T>>;
  52. // Fetches the result of the provided |async_operation| and corresponding
  53. // |async_status| and assigns that value to |result|. Returns an HRESULT
  54. // indicating the success of the operation.
  55. template <typename T>
  56. HRESULT GetAsyncResultsT(IAsyncOperationT<T>* async_operation,
  57. AsyncStatus async_status,
  58. AsyncResultsT<T>* results) {
  59. if (async_status == AsyncStatus::Completed) {
  60. // To expose |results| to GetResults as the expected type, this call first
  61. // dereferences |results| from ComPtr<T>* or T* to ComPtr<T> or T
  62. // respectively, then requests the address, converting to T** or T*
  63. // respectively.
  64. HRESULT hr = async_operation->GetResults(&(*results));
  65. if (FAILED(hr))
  66. *results = AsyncResultsT<T>{};
  67. return hr;
  68. }
  69. *results = AsyncResultsT<T>{};
  70. Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> async_info;
  71. HRESULT hr = async_operation->QueryInterface(IID_PPV_ARGS(&async_info));
  72. if (FAILED(hr))
  73. return hr;
  74. HRESULT operation_hr;
  75. hr = async_info->get_ErrorCode(&operation_hr);
  76. if (FAILED(hr))
  77. return hr;
  78. DCHECK(FAILED(operation_hr));
  79. return operation_hr;
  80. }
  81. // Registers an internal completion handler for |async_operation| and upon
  82. // completion, posts the results to the provided |completed_handler|. Returns an
  83. // HRESULT indicating the success of registering the internal completion
  84. // handler.
  85. //
  86. // Callers need to ensure that this method is invoked in the correct COM
  87. // apartment, i.e. the one that created |async_operation|. The
  88. // |completed_handler| will be run on the same sequence that invoked this
  89. // method. This call does not ensure the lifetime of the |async_operation|,
  90. // which must be done by the caller.
  91. template <typename T>
  92. HRESULT PostAsyncOperationCompletedHandler(
  93. IAsyncOperationT<T>* async_operation,
  94. IAsyncOperationCompletedHandlerT<T> completed_handler) {
  95. auto internal_completed_handler = base::BindOnce(
  96. [](scoped_refptr<base::SequencedTaskRunner> task_runner,
  97. IAsyncOperationCompletedHandlerT<T> completed_handler,
  98. IAsyncOperationT<T>* async_operation, AsyncStatus async_status) {
  99. // The raw |async_operation| pointer received as part of this
  100. // CompletedHandler is only guaranteed to be valid for the lifetime of
  101. // this call, so to ensure it is still valid through the lifetime of the
  102. // call to the |completed_handler| we capture it in an appropriate
  103. // ref-counted pointer.
  104. Microsoft::WRL::ComPtr<IAsyncOperationT<T>> ref_counted_async_operation(
  105. async_operation);
  106. // Posting the results to the TaskRunner is required, since this
  107. // CompletedHandler might be invoked on an arbitrary thread.
  108. task_runner->PostTask(
  109. FROM_HERE,
  110. BindOnce(
  111. [](IAsyncOperationCompletedHandlerT<T> completed_handler,
  112. Microsoft::WRL::ComPtr<IAsyncOperationT<T>> async_operation,
  113. AsyncStatus async_status) {
  114. std::move(completed_handler)
  115. .Run(async_operation.Get(), async_status);
  116. },
  117. std::move(completed_handler), ref_counted_async_operation,
  118. async_status));
  119. return S_OK;
  120. },
  121. base::SequencedTaskRunnerHandle::Get(), std::move(completed_handler));
  122. using CompletedHandler = Microsoft::WRL::Implements<
  123. Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
  124. ABI::Windows::Foundation::IAsyncOperationCompletedHandler<T>,
  125. Microsoft::WRL::FtmBase>;
  126. return async_operation->put_Completed(
  127. Microsoft::WRL::Callback<CompletedHandler>(
  128. [internal_completed_handler(std::move(internal_completed_handler))](
  129. IAsyncOperationT<T>* async_operation,
  130. AsyncStatus async_status) mutable {
  131. return std::move(internal_completed_handler)
  132. .Run(async_operation, async_status);
  133. })
  134. .Get());
  135. }
  136. } // namespace internal
  137. // Registers an internal completion handler for |async_operation| and upon
  138. // successful completion invokes the |success_callback| with the result. If the
  139. // |async_operation| encounters an error no callback will be invoked. Returns
  140. // an HRESULT indicating the success of registering the internal completion
  141. // handler.
  142. //
  143. // Callers need to ensure that this method is invoked in the correct COM
  144. // apartment, i.e. the one that created |async_operation|. The resulting
  145. // callback (i.e. |success_callback|) will be run on the same sequence that
  146. // invoked this method. This call does not ensure the lifetime of the
  147. // |async_operation|, which must be done by the caller.
  148. template <typename T>
  149. HRESULT PostAsyncHandlers(
  150. internal::IAsyncOperationT<T>* async_operation,
  151. base::OnceCallback<void(internal::AsyncResultsT<T>)> success_callback) {
  152. return internal::PostAsyncOperationCompletedHandler(
  153. async_operation,
  154. base::BindOnce(
  155. [](base::OnceCallback<void(internal::AsyncResultsT<T>)>
  156. success_callback,
  157. internal::IAsyncOperationT<T>* async_operation,
  158. AsyncStatus async_status) {
  159. internal::AsyncResultsT<T> results;
  160. HRESULT hr = internal::GetAsyncResultsT(async_operation,
  161. async_status, &results);
  162. if (SUCCEEDED(hr))
  163. std::move(success_callback).Run(results);
  164. },
  165. std::move(success_callback)));
  166. }
  167. // Registers an internal completion handler for |async_operation| and upon
  168. // successful completion invokes the |success_callback| with the result. If the
  169. // |async_operation| encounters an error the |failure_callback| will instead be
  170. // invoked. Returns an HRESULT indicating the success of registering the
  171. // internal completion handler.
  172. //
  173. // Callers need to ensure that this method is invoked in the correct COM
  174. // apartment, i.e. the one that created |async_operation|. The resulting
  175. // callback (|success_callback| or |failure_callback|) will be run on the same
  176. // sequence that invoked this method. This call does not ensure the lifetime of
  177. // the |async_operation|, which must be done by the caller.
  178. template <typename T>
  179. HRESULT PostAsyncHandlers(
  180. internal::IAsyncOperationT<T>* async_operation,
  181. base::OnceCallback<void(internal::AsyncResultsT<T>)> success_callback,
  182. base::OnceCallback<void()> failure_callback) {
  183. return internal::PostAsyncOperationCompletedHandler(
  184. async_operation,
  185. base::BindOnce(
  186. [](base::OnceCallback<void(internal::AsyncResultsT<T>)>
  187. success_callback,
  188. base::OnceCallback<void()> failure_callback,
  189. internal::IAsyncOperationT<T>* async_operation,
  190. AsyncStatus async_status) {
  191. internal::AsyncResultsT<T> results;
  192. HRESULT hr = internal::GetAsyncResultsT(async_operation,
  193. async_status, &results);
  194. if (SUCCEEDED(hr))
  195. std::move(success_callback).Run(results);
  196. else
  197. std::move(failure_callback).Run();
  198. },
  199. std::move(success_callback), std::move(failure_callback)));
  200. }
  201. // Registers an internal completion handler for |async_operation| and upon
  202. // successful completion invokes the |success_callback| with the result. If the
  203. // |async_operation| encounters an error the |failure_callback| will instead be
  204. // invoked with the failing HRESULT. Returns an HRESULT indicating the success
  205. // of registering the internal completion handler.
  206. //
  207. // Callers need to ensure that this method is invoked in the correct COM
  208. // apartment, i.e. the one that created |async_operation|. The resulting
  209. // callback (|success_callback| or |failure_callback|) will be run on the same
  210. // sequence that invoked this method. This call does not ensure the lifetime of
  211. // the |async_operation|, which must be done by the caller.
  212. template <typename T>
  213. HRESULT PostAsyncHandlers(
  214. internal::IAsyncOperationT<T>* async_operation,
  215. base::OnceCallback<void(internal::AsyncResultsT<T>)> success_callback,
  216. base::OnceCallback<void(HRESULT)> failure_callback) {
  217. return internal::PostAsyncOperationCompletedHandler(
  218. async_operation,
  219. base::BindOnce(
  220. [](base::OnceCallback<void(internal::AsyncResultsT<T>)>
  221. success_callback,
  222. base::OnceCallback<void(HRESULT)> failure_callback,
  223. internal::IAsyncOperationT<T>* async_operation,
  224. AsyncStatus async_status) {
  225. internal::AsyncResultsT<T> results;
  226. HRESULT hr = internal::GetAsyncResultsT(async_operation,
  227. async_status, &results);
  228. if (SUCCEEDED(hr))
  229. std::move(success_callback).Run(results);
  230. else
  231. std::move(failure_callback).Run(hr);
  232. },
  233. std::move(success_callback), std::move(failure_callback)));
  234. }
  235. // Registers an internal completion handler for |async_operation| and upon
  236. // successful completion invokes the |success_callback| with the result. If the
  237. // |async_operation| encounters an error the |failure_callback| will instead be
  238. // invoked with the result and an HRESULT indicating the success of fetching
  239. // that result (NOT an HRESULT expressing the failure of the operation). Returns
  240. // an HRESULT indicating the success of registering the internal completion
  241. // handler.
  242. //
  243. // This overload is designed for (uncommon) operations whose results encapsulate
  244. // success and failure information (and as a result of that are expected to be
  245. // available under both success and failure conditions).
  246. //
  247. // Callers need to ensure that this method is invoked in the correct COM
  248. // apartment, i.e. the one that created |async_operation|. The resulting
  249. // callback (|success_callback| or |failure_callback|) will be run on the same
  250. // sequence that invoked this method. This call does not ensure the lifetime of
  251. // the |async_operation|, which must be done by the caller.
  252. template <typename T>
  253. HRESULT PostAsyncHandlers(
  254. internal::IAsyncOperationT<T>* async_operation,
  255. base::OnceCallback<void(internal::AsyncResultsT<T>)> success_callback,
  256. base::OnceCallback<void(HRESULT, internal::AsyncResultsT<T>)>
  257. failure_callback) {
  258. return internal::PostAsyncOperationCompletedHandler(
  259. async_operation,
  260. base::BindOnce(
  261. [](base::OnceCallback<void(internal::AsyncResultsT<T>)>
  262. success_callback,
  263. base::OnceCallback<void(HRESULT, internal::AsyncResultsT<T>)>
  264. failure_callback,
  265. internal::IAsyncOperationT<T>* async_operation,
  266. AsyncStatus async_status) {
  267. internal::AsyncResultsT<T> results;
  268. HRESULT hr = internal::GetAsyncResultsT(
  269. async_operation,
  270. async_status == AsyncStatus::Error ? AsyncStatus::Completed
  271. : async_status,
  272. &results);
  273. if (SUCCEEDED(hr) && async_status == AsyncStatus::Completed)
  274. std::move(success_callback).Run(results);
  275. else
  276. std::move(failure_callback).Run(hr, results);
  277. },
  278. std::move(success_callback), std::move(failure_callback)));
  279. }
  280. // Deprecated.
  281. //
  282. // Registers an internal completion handler for |async_operation| and upon
  283. // invocation, posts the results to the provided |callback|. Returns an HRESULT
  284. // indicating the success of registering the internal completion handler.
  285. //
  286. // Callers need to ensure that this method is invoked in the correct COM
  287. // apartment, i.e. the one that created |async_operation|. The |callback| will
  288. // be run on the same sequence that invoked this method.
  289. //
  290. // WARNING: This call holds a reference to the provided |async_operation| until
  291. // it completes.
  292. template <typename T>
  293. HRESULT PostAsyncResults(
  294. Microsoft::WRL::ComPtr<internal::IAsyncOperationT<T>> async_operation,
  295. base::OnceCallback<void(internal::AsyncResultsT<T>)> callback) {
  296. return internal::PostAsyncOperationCompletedHandler(
  297. async_operation.Get(),
  298. base::BindOnce(
  299. [](Microsoft::WRL::ComPtr<internal::IAsyncOperationT<T>>
  300. original_async_operation,
  301. base::OnceCallback<void(internal::AsyncResultsT<T>)> callback,
  302. internal::IAsyncOperationT<T>* async_operation,
  303. AsyncStatus async_status) {
  304. DCHECK(original_async_operation.Get() == async_operation);
  305. if (async_status != AsyncStatus::Completed) {
  306. VLOG(2) << "Got unexpected AsyncStatus: "
  307. << internal::ToCString(async_status);
  308. }
  309. internal::AsyncResultsT<T> results;
  310. HRESULT hr = internal::GetAsyncResultsT(async_operation,
  311. async_status, &results);
  312. if (FAILED(hr)) {
  313. VLOG(2) << "GetAsyncResultsT failed: "
  314. << logging::SystemErrorCodeToString(hr);
  315. }
  316. std::move(callback).Run(results);
  317. },
  318. async_operation, std::move(callback)));
  319. }
  320. } // namespace win
  321. } // namespace base
  322. #endif // BASE_WIN_POST_ASYNC_RESULTS_H_