post_async_results.h 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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/threading/thread_task_runner_handle.h"
  16. namespace base {
  17. namespace win {
  18. namespace internal {
  19. // Utility function to pretty print enum values.
  20. constexpr const char* ToCString(AsyncStatus async_status) {
  21. switch (async_status) {
  22. case AsyncStatus::Started:
  23. return "AsyncStatus::Started";
  24. case AsyncStatus::Completed:
  25. return "AsyncStatus::Completed";
  26. case AsyncStatus::Canceled:
  27. return "AsyncStatus::Canceled";
  28. case AsyncStatus::Error:
  29. return "AsyncStatus::Error";
  30. }
  31. NOTREACHED();
  32. return "";
  33. }
  34. template <typename T>
  35. using AsyncAbiT = typename ABI::Windows::Foundation::Internal::GetAbiType<
  36. typename ABI::Windows::Foundation::IAsyncOperation<T>::TResult_complex>::
  37. type;
  38. // Compile time switch to decide what container to use for the async results for
  39. // |T|. Depends on whether the underlying Abi type is a pointer to IUnknown or
  40. // not. It queries the internals of Windows::Foundation to obtain this
  41. // information.
  42. template <typename T>
  43. using AsyncResultsT = std::conditional_t<
  44. std::is_convertible<AsyncAbiT<T>, IUnknown*>::value,
  45. Microsoft::WRL::ComPtr<std::remove_pointer_t<AsyncAbiT<T>>>,
  46. AsyncAbiT<T>>;
  47. // Obtains the results of the provided async operation.
  48. template <typename T>
  49. AsyncResultsT<T> GetAsyncResults(
  50. ABI::Windows::Foundation::IAsyncOperation<T>* async_op) {
  51. AsyncResultsT<T> results;
  52. HRESULT hr = async_op->GetResults(&results);
  53. if (FAILED(hr)) {
  54. VLOG(2) << "GetAsyncResults failed: "
  55. << logging::SystemErrorCodeToString(hr);
  56. }
  57. return results;
  58. }
  59. } // namespace internal
  60. // This method registers a completion handler for |async_op| and will post the
  61. // results to |callback|. The |callback| will be run on the same thread that
  62. // invoked this method. Callers need to ensure that this method is invoked in
  63. // the correct COM apartment, i.e. the one that created |async_op|. While a WRL
  64. // Callback can be constructed from callable types such as a lambda or
  65. // std::function objects, it cannot be directly constructed from a
  66. // base::OnceCallback. Thus the callback is moved into a capturing lambda, which
  67. // then posts the callback once it is run. Posting the results to the TaskRunner
  68. // is required, since the completion callback might be invoked on an arbitrary
  69. // thread. Lastly, the lambda takes ownership of |async_op|, as this needs to be
  70. // kept alive until GetAsyncResults can be invoked.
  71. template <typename T>
  72. HRESULT PostAsyncResults(
  73. Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<T>>
  74. async_op,
  75. base::OnceCallback<void(internal::AsyncResultsT<T>)> callback) {
  76. auto completed_cb = base::BindOnce(
  77. [](Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<T>>
  78. async_op,
  79. base::OnceCallback<void(internal::AsyncResultsT<T>)> callback) {
  80. std::move(callback).Run(internal::GetAsyncResults(async_op.Get()));
  81. },
  82. async_op, std::move(callback));
  83. auto completed_lambda = [task_runner(base::ThreadTaskRunnerHandle::Get()),
  84. completed_cb(std::move(completed_cb))](
  85. auto&&, AsyncStatus async_status) mutable {
  86. if (async_status != AsyncStatus::Completed) {
  87. VLOG(2) << "Got unexpected AsyncStatus: "
  88. << internal::ToCString(async_status);
  89. }
  90. // Note: We are ignoring the passed in pointer to |async_op|, as |callback|
  91. // has access to the initially provided |async_op|. Since the code within
  92. // the lambda could be executed on any thread, it is vital that the
  93. // callback gets posted to the original |task_runner|, as this is
  94. // guaranteed to be in the correct COM apartment.
  95. task_runner->PostTask(FROM_HERE, std::move(completed_cb));
  96. return S_OK;
  97. };
  98. using CompletedHandler = Microsoft::WRL::Implements<
  99. Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
  100. ABI::Windows::Foundation::IAsyncOperationCompletedHandler<T>,
  101. Microsoft::WRL::FtmBase>;
  102. return async_op->put_Completed(
  103. Microsoft::WRL::Callback<CompletedHandler>(std::move(completed_lambda))
  104. .Get());
  105. }
  106. } // namespace win
  107. } // namespace base
  108. #endif // BASE_WIN_POST_ASYNC_RESULTS_H_