post_async_results.h 4.7 KB

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