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