123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- // Copyright 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef BASE_WIN_POST_ASYNC_RESULTS_H_
- #define BASE_WIN_POST_ASYNC_RESULTS_H_
- #include <unknwn.h>
- #include <windows.foundation.h>
- #include <wrl/async.h>
- #include <wrl/client.h>
- #include <type_traits>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/location.h"
- #include "base/logging.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- namespace base {
- namespace win {
- namespace internal {
- // Utility function to pretty print enum values.
- constexpr const char* ToCString(AsyncStatus async_status) {
- switch (async_status) {
- case AsyncStatus::Started:
- return "AsyncStatus::Started";
- case AsyncStatus::Completed:
- return "AsyncStatus::Completed";
- case AsyncStatus::Canceled:
- return "AsyncStatus::Canceled";
- case AsyncStatus::Error:
- return "AsyncStatus::Error";
- }
- NOTREACHED();
- return "";
- }
- template <typename T>
- using IAsyncOperationT = typename ABI::Windows::Foundation::IAsyncOperation<T>;
- template <typename T>
- using IAsyncOperationCompletedHandlerT =
- typename base::OnceCallback<void(IAsyncOperationT<T>*, AsyncStatus)>;
- template <typename T>
- using AsyncAbiT = typename ABI::Windows::Foundation::Internal::GetAbiType<
- typename IAsyncOperationT<T>::TResult_complex>::type;
- // Compile time switch to decide what container to use for the async results for
- // |T|. Depends on whether the underlying Abi type is a pointer to IUnknown or
- // not. It queries the internals of Windows::Foundation to obtain this
- // information.
- template <typename T>
- using AsyncResultsT = std::conditional_t<
- std::is_convertible<AsyncAbiT<T>, IUnknown*>::value,
- Microsoft::WRL::ComPtr<std::remove_pointer_t<AsyncAbiT<T>>>,
- AsyncAbiT<T>>;
- // Fetches the result of the provided |async_operation| and corresponding
- // |async_status| and assigns that value to |result|. Returns an HRESULT
- // indicating the success of the operation.
- template <typename T>
- HRESULT GetAsyncResultsT(IAsyncOperationT<T>* async_operation,
- AsyncStatus async_status,
- AsyncResultsT<T>* results) {
- if (async_status == AsyncStatus::Completed) {
- // To expose |results| to GetResults as the expected type, this call first
- // dereferences |results| from ComPtr<T>* or T* to ComPtr<T> or T
- // respectively, then requests the address, converting to T** or T*
- // respectively.
- HRESULT hr = async_operation->GetResults(&(*results));
- if (FAILED(hr))
- *results = AsyncResultsT<T>{};
- return hr;
- }
- *results = AsyncResultsT<T>{};
- Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> async_info;
- HRESULT hr = async_operation->QueryInterface(IID_PPV_ARGS(&async_info));
- if (FAILED(hr))
- return hr;
- HRESULT operation_hr;
- hr = async_info->get_ErrorCode(&operation_hr);
- if (FAILED(hr))
- return hr;
- DCHECK(FAILED(operation_hr));
- return operation_hr;
- }
- // Registers an internal completion handler for |async_operation| and upon
- // completion, posts the results to the provided |completed_handler|. Returns an
- // HRESULT indicating the success of registering the internal completion
- // handler.
- //
- // Callers need to ensure that this method is invoked in the correct COM
- // apartment, i.e. the one that created |async_operation|. The
- // |completed_handler| will be run on the same sequence that invoked this
- // method. This call does not ensure the lifetime of the |async_operation|,
- // which must be done by the caller.
- template <typename T>
- HRESULT PostAsyncOperationCompletedHandler(
- IAsyncOperationT<T>* async_operation,
- IAsyncOperationCompletedHandlerT<T> completed_handler) {
- auto internal_completed_handler = base::BindOnce(
- [](scoped_refptr<base::SequencedTaskRunner> task_runner,
- IAsyncOperationCompletedHandlerT<T> completed_handler,
- IAsyncOperationT<T>* async_operation, AsyncStatus async_status) {
- // The raw |async_operation| pointer received as part of this
- // CompletedHandler is only guaranteed to be valid for the lifetime of
- // this call, so to ensure it is still valid through the lifetime of the
- // call to the |completed_handler| we capture it in an appropriate
- // ref-counted pointer.
- Microsoft::WRL::ComPtr<IAsyncOperationT<T>> ref_counted_async_operation(
- async_operation);
- // Posting the results to the TaskRunner is required, since this
- // CompletedHandler might be invoked on an arbitrary thread.
- task_runner->PostTask(
- FROM_HERE,
- BindOnce(
- [](IAsyncOperationCompletedHandlerT<T> completed_handler,
- Microsoft::WRL::ComPtr<IAsyncOperationT<T>> async_operation,
- AsyncStatus async_status) {
- std::move(completed_handler)
- .Run(async_operation.Get(), async_status);
- },
- std::move(completed_handler), ref_counted_async_operation,
- async_status));
- return S_OK;
- },
- base::SequencedTaskRunnerHandle::Get(), std::move(completed_handler));
- using CompletedHandler = Microsoft::WRL::Implements<
- Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
- ABI::Windows::Foundation::IAsyncOperationCompletedHandler<T>,
- Microsoft::WRL::FtmBase>;
- return async_operation->put_Completed(
- Microsoft::WRL::Callback<CompletedHandler>(
- [internal_completed_handler(std::move(internal_completed_handler))](
- IAsyncOperationT<T>* async_operation,
- AsyncStatus async_status) mutable {
- return std::move(internal_completed_handler)
- .Run(async_operation, async_status);
- })
- .Get());
- }
- } // namespace internal
- // Registers an internal completion handler for |async_operation| and upon
- // successful completion invokes the |success_callback| with the result. If the
- // |async_operation| encounters an error no callback will be invoked. Returns
- // an HRESULT indicating the success of registering the internal completion
- // handler.
- //
- // Callers need to ensure that this method is invoked in the correct COM
- // apartment, i.e. the one that created |async_operation|. The resulting
- // callback (i.e. |success_callback|) will be run on the same sequence that
- // invoked this method. This call does not ensure the lifetime of the
- // |async_operation|, which must be done by the caller.
- template <typename T>
- HRESULT PostAsyncHandlers(
- internal::IAsyncOperationT<T>* async_operation,
- base::OnceCallback<void(internal::AsyncResultsT<T>)> success_callback) {
- return internal::PostAsyncOperationCompletedHandler(
- async_operation,
- base::BindOnce(
- [](base::OnceCallback<void(internal::AsyncResultsT<T>)>
- success_callback,
- internal::IAsyncOperationT<T>* async_operation,
- AsyncStatus async_status) {
- internal::AsyncResultsT<T> results;
- HRESULT hr = internal::GetAsyncResultsT(async_operation,
- async_status, &results);
- if (SUCCEEDED(hr))
- std::move(success_callback).Run(results);
- },
- std::move(success_callback)));
- }
- // Registers an internal completion handler for |async_operation| and upon
- // successful completion invokes the |success_callback| with the result. If the
- // |async_operation| encounters an error the |failure_callback| will instead be
- // invoked. Returns an HRESULT indicating the success of registering the
- // internal completion handler.
- //
- // Callers need to ensure that this method is invoked in the correct COM
- // apartment, i.e. the one that created |async_operation|. The resulting
- // callback (|success_callback| or |failure_callback|) will be run on the same
- // sequence that invoked this method. This call does not ensure the lifetime of
- // the |async_operation|, which must be done by the caller.
- template <typename T>
- HRESULT PostAsyncHandlers(
- internal::IAsyncOperationT<T>* async_operation,
- base::OnceCallback<void(internal::AsyncResultsT<T>)> success_callback,
- base::OnceCallback<void()> failure_callback) {
- return internal::PostAsyncOperationCompletedHandler(
- async_operation,
- base::BindOnce(
- [](base::OnceCallback<void(internal::AsyncResultsT<T>)>
- success_callback,
- base::OnceCallback<void()> failure_callback,
- internal::IAsyncOperationT<T>* async_operation,
- AsyncStatus async_status) {
- internal::AsyncResultsT<T> results;
- HRESULT hr = internal::GetAsyncResultsT(async_operation,
- async_status, &results);
- if (SUCCEEDED(hr))
- std::move(success_callback).Run(results);
- else
- std::move(failure_callback).Run();
- },
- std::move(success_callback), std::move(failure_callback)));
- }
- // Registers an internal completion handler for |async_operation| and upon
- // successful completion invokes the |success_callback| with the result. If the
- // |async_operation| encounters an error the |failure_callback| will instead be
- // invoked with the failing HRESULT. Returns an HRESULT indicating the success
- // of registering the internal completion handler.
- //
- // Callers need to ensure that this method is invoked in the correct COM
- // apartment, i.e. the one that created |async_operation|. The resulting
- // callback (|success_callback| or |failure_callback|) will be run on the same
- // sequence that invoked this method. This call does not ensure the lifetime of
- // the |async_operation|, which must be done by the caller.
- template <typename T>
- HRESULT PostAsyncHandlers(
- internal::IAsyncOperationT<T>* async_operation,
- base::OnceCallback<void(internal::AsyncResultsT<T>)> success_callback,
- base::OnceCallback<void(HRESULT)> failure_callback) {
- return internal::PostAsyncOperationCompletedHandler(
- async_operation,
- base::BindOnce(
- [](base::OnceCallback<void(internal::AsyncResultsT<T>)>
- success_callback,
- base::OnceCallback<void(HRESULT)> failure_callback,
- internal::IAsyncOperationT<T>* async_operation,
- AsyncStatus async_status) {
- internal::AsyncResultsT<T> results;
- HRESULT hr = internal::GetAsyncResultsT(async_operation,
- async_status, &results);
- if (SUCCEEDED(hr))
- std::move(success_callback).Run(results);
- else
- std::move(failure_callback).Run(hr);
- },
- std::move(success_callback), std::move(failure_callback)));
- }
- // Registers an internal completion handler for |async_operation| and upon
- // successful completion invokes the |success_callback| with the result. If the
- // |async_operation| encounters an error the |failure_callback| will instead be
- // invoked with the result and an HRESULT indicating the success of fetching
- // that result (NOT an HRESULT expressing the failure of the operation). Returns
- // an HRESULT indicating the success of registering the internal completion
- // handler.
- //
- // This overload is designed for (uncommon) operations whose results encapsulate
- // success and failure information (and as a result of that are expected to be
- // available under both success and failure conditions).
- //
- // Callers need to ensure that this method is invoked in the correct COM
- // apartment, i.e. the one that created |async_operation|. The resulting
- // callback (|success_callback| or |failure_callback|) will be run on the same
- // sequence that invoked this method. This call does not ensure the lifetime of
- // the |async_operation|, which must be done by the caller.
- template <typename T>
- HRESULT PostAsyncHandlers(
- internal::IAsyncOperationT<T>* async_operation,
- base::OnceCallback<void(internal::AsyncResultsT<T>)> success_callback,
- base::OnceCallback<void(HRESULT, internal::AsyncResultsT<T>)>
- failure_callback) {
- return internal::PostAsyncOperationCompletedHandler(
- async_operation,
- base::BindOnce(
- [](base::OnceCallback<void(internal::AsyncResultsT<T>)>
- success_callback,
- base::OnceCallback<void(HRESULT, internal::AsyncResultsT<T>)>
- failure_callback,
- internal::IAsyncOperationT<T>* async_operation,
- AsyncStatus async_status) {
- internal::AsyncResultsT<T> results;
- HRESULT hr = internal::GetAsyncResultsT(
- async_operation,
- async_status == AsyncStatus::Error ? AsyncStatus::Completed
- : async_status,
- &results);
- if (SUCCEEDED(hr) && async_status == AsyncStatus::Completed)
- std::move(success_callback).Run(results);
- else
- std::move(failure_callback).Run(hr, results);
- },
- std::move(success_callback), std::move(failure_callback)));
- }
- // Deprecated.
- //
- // Registers an internal completion handler for |async_operation| and upon
- // invocation, posts the results to the provided |callback|. Returns an HRESULT
- // indicating the success of registering the internal completion handler.
- //
- // Callers need to ensure that this method is invoked in the correct COM
- // apartment, i.e. the one that created |async_operation|. The |callback| will
- // be run on the same sequence that invoked this method.
- //
- // WARNING: This call holds a reference to the provided |async_operation| until
- // it completes.
- template <typename T>
- HRESULT PostAsyncResults(
- Microsoft::WRL::ComPtr<internal::IAsyncOperationT<T>> async_operation,
- base::OnceCallback<void(internal::AsyncResultsT<T>)> callback) {
- return internal::PostAsyncOperationCompletedHandler(
- async_operation.Get(),
- base::BindOnce(
- [](Microsoft::WRL::ComPtr<internal::IAsyncOperationT<T>>
- original_async_operation,
- base::OnceCallback<void(internal::AsyncResultsT<T>)> callback,
- internal::IAsyncOperationT<T>* async_operation,
- AsyncStatus async_status) {
- DCHECK(original_async_operation.Get() == async_operation);
- if (async_status != AsyncStatus::Completed) {
- VLOG(2) << "Got unexpected AsyncStatus: "
- << internal::ToCString(async_status);
- }
- internal::AsyncResultsT<T> results;
- HRESULT hr = internal::GetAsyncResultsT(async_operation,
- async_status, &results);
- if (FAILED(hr)) {
- VLOG(2) << "GetAsyncResultsT failed: "
- << logging::SystemErrorCodeToString(hr);
- }
- std::move(callback).Run(results);
- },
- async_operation, std::move(callback)));
- }
- } // namespace win
- } // namespace base
- #endif // BASE_WIN_POST_ASYNC_RESULTS_H_
|