// Copyright 2018 The Chromium Authors // 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 #include #include #include #include #include #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/location.h" #include "base/logging.h" #include "base/task/bind_post_task.h" #include "base/task/sequenced_task_runner.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(); } template using IAsyncOperationT = typename ABI::Windows::Foundation::IAsyncOperation; template using IAsyncOperationCompletedHandlerT = typename base::OnceCallback*, AsyncStatus)>; template using AsyncAbiT = typename ABI::Windows::Foundation::Internal::GetAbiType< typename IAsyncOperationT::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 using AsyncResultsT = std::conditional_t< std::is_convertible_v, IUnknown*>, Microsoft::WRL::ComPtr>>, AsyncAbiT>; // 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 HRESULT GetAsyncResultsT(IAsyncOperationT* async_operation, AsyncStatus async_status, AsyncResultsT* results) { if (async_status == AsyncStatus::Completed) { // To expose |results| to GetResults as the expected type, this call first // dereferences |results| from ComPtr* or T* to ComPtr or T // respectively, then requests the address, converting to T** or T* // respectively. HRESULT hr = async_operation->GetResults(&(*results)); if (FAILED(hr)) *results = AsyncResultsT{}; return hr; } *results = AsyncResultsT{}; Microsoft::WRL::ComPtr 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 HRESULT PostAsyncOperationCompletedHandler( IAsyncOperationT* async_operation, IAsyncOperationCompletedHandlerT completed_handler) { using AsyncResult = std::pair>, AsyncStatus>; auto internal_completed_handler = base::BindOnce([](IAsyncOperationT* async_operation, AsyncStatus async_status) -> AsyncResult { // Posting the results to the TaskRunner is required, since this // CompletedHandler might be invoked on an arbitrary thread however // the raw |async_operation| pointer 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. return std::make_pair(async_operation, async_status); }) .Then( base::BindPostTaskToCurrentDefault(base::BindOnce( [](IAsyncOperationCompletedHandlerT completed_handler, AsyncResult async_result) { std::move(completed_handler) .Run(async_result.first.Get(), async_result.second); }, std::move(completed_handler)))); using CompletedHandler = Microsoft::WRL::Implements< Microsoft::WRL::RuntimeClassFlags, ABI::Windows::Foundation::IAsyncOperationCompletedHandler, Microsoft::WRL::FtmBase>; return async_operation->put_Completed( Microsoft::WRL::Callback( [internal_completed_handler(std::move(internal_completed_handler))]( IAsyncOperationT* async_operation, AsyncStatus async_status) mutable { std::move(internal_completed_handler) .Run(async_operation, async_status); return S_OK; }) .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 HRESULT PostAsyncHandlers( internal::IAsyncOperationT* async_operation, base::OnceCallback)> success_callback) { return internal::PostAsyncOperationCompletedHandler( async_operation, base::BindOnce( [](base::OnceCallback)> success_callback, internal::IAsyncOperationT* async_operation, AsyncStatus async_status) { internal::AsyncResultsT 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 HRESULT PostAsyncHandlers( internal::IAsyncOperationT* async_operation, base::OnceCallback)> success_callback, base::OnceCallback failure_callback) { return internal::PostAsyncOperationCompletedHandler( async_operation, base::BindOnce( [](base::OnceCallback)> success_callback, base::OnceCallback failure_callback, internal::IAsyncOperationT* async_operation, AsyncStatus async_status) { internal::AsyncResultsT 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 HRESULT PostAsyncHandlers( internal::IAsyncOperationT* async_operation, base::OnceCallback)> success_callback, base::OnceCallback failure_callback) { return internal::PostAsyncOperationCompletedHandler( async_operation, base::BindOnce( [](base::OnceCallback)> success_callback, base::OnceCallback failure_callback, internal::IAsyncOperationT* async_operation, AsyncStatus async_status) { internal::AsyncResultsT 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 HRESULT PostAsyncHandlers( internal::IAsyncOperationT* async_operation, base::OnceCallback)> success_callback, base::OnceCallback)> failure_callback) { return internal::PostAsyncOperationCompletedHandler( async_operation, base::BindOnce( [](base::OnceCallback)> success_callback, base::OnceCallback)> failure_callback, internal::IAsyncOperationT* async_operation, AsyncStatus async_status) { internal::AsyncResultsT 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 HRESULT PostAsyncResults( Microsoft::WRL::ComPtr> async_operation, base::OnceCallback)> callback) { return internal::PostAsyncOperationCompletedHandler( async_operation.Get(), base::BindOnce( [](Microsoft::WRL::ComPtr> original_async_operation, base::OnceCallback)> callback, internal::IAsyncOperationT* 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 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_