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