// 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_ASYNC_OPERATION_H_ #define BASE_WIN_ASYNC_OPERATION_H_ #include #include #include #include #include #include #include "base/bind.h" #include "base/callback.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/threading/thread_checker.h" namespace base { namespace win { // This file provides an implementation of Windows::Foundation::IAsyncOperation. // Specializations exist for "regular" types and interface types that inherit // from IUnknown. Both specializations expose a callback() method, which can be // used to provide the result that will be forwarded to the registered // completion handler. For regular types it expects an instance of that type, // and for interface types it expects a corresponding ComPtr. This class is // thread-affine and all member methods should be called on the same thread that // constructed the object. In order to offload heavy result computation, // base's PostTaskAndReplyWithResult() should be used with the ResultCallback // passed as a reply. // // Example usages: // // // Regular types // auto regular_op = WRL::Make>(); // auto cb = regular_op->callback(); // regular_op->put_Completed(...event handler...); // ... // // This will invoke the event handler. // std::move(cb).Run(123); // ... // // Results can be queried: // int results = 0; // regular_op->GetResults(&results); // EXPECT_EQ(123, results); // // // Interface types // auto interface_op = WRL::Make>(); // auto cb = interface_op->callback(); // interface_op->put_Completed(...event handler...); // ... // // This will invoke the event handler. // std::move(cb).Run(WRL::Make()); // ... // // Results can be queried: // WRL::ComPtr results; // interface_op->GetResults(&results); // // |results| points to the provided IFooBarImpl instance. // // // Offloading a heavy computation: // auto my_op = WRL::Make>(); // base::PostTaskAndReplyWithResult( // base::BindOnce(MakeFooBar), my_op->callback()); namespace internal { // Template tricks needed to dispatch to the correct implementation below. // // For all types which are neither InterfaceGroups nor RuntimeClasses, the // following three typedefs are synonyms for a single C++ type. But for // InterfaceGroups and RuntimeClasses, they are different types: // LogicalT: The C++ Type for the InterfaceGroup or RuntimeClass, when // used as a template parameter. Eg "RCFoo*" // AbiT: The C++ type for the default interface used to represent the // InterfaceGroup or RuntimeClass when passed as a method parameter. // Eg "IFoo*" // ComplexT: An instantiation of the Internal "AggregateType" template that // combines LogicalT with AbiT. Eg "AggregateType" // // windows.foundation.collections.h defines the following template and // semantics in Windows::Foundation::Internal: // // template // struct AggregateType; // // LogicalType - the Windows Runtime type (eg, runtime class, inteface group, // etc) being provided as an argument to an _impl template, when // that type cannot be represented at the ABI. // AbiType - the type used for marshalling, ie "at the ABI", for the // logical type. template using ComplexT = typename ABI::Windows::Foundation::IAsyncOperation::TResult_complex; template using AbiT = typename ABI::Windows::Foundation::Internal::GetAbiType>::type; template using LogicalT = typename ABI::Windows::Foundation::Internal::GetLogicalType< ComplexT>::type; template using InterfaceT = std::remove_pointer_t>; // 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 ResultT = std::conditional_t, IUnknown*>::value, Microsoft::WRL::ComPtr>>, AbiT>; template using StorageT = std::conditional_t, IUnknown*>::value, Microsoft::WRL::ComPtr>>, base::Optional>>; template HRESULT CopyStorage(const Microsoft::WRL::ComPtr& storage, T** results) { return storage.CopyTo(results); } template HRESULT CopyStorage(const base::Optional& storage, T* results) { *results = *storage; return S_OK; } } // namespace internal template class AsyncOperation : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::IAsyncOperation> { public: using StorageT = internal::StorageT; using ResultT = internal::ResultT; using Handler = ABI::Windows::Foundation::IAsyncOperationCompletedHandler; using ResultCallback = base::OnceCallback; AsyncOperation() : weak_factory_(this) { // Note: This can't be done in the constructor initializer list. This is // because it relies on weak_factory_ to be initialized, which needs to be // the last class member. Also applies below. callback_ = base::BindOnce(&AsyncOperation::OnResult, weak_factory_.GetWeakPtr()); } ~AsyncOperation() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); } // ABI::Windows::Foundation::IAsyncOperation: IFACEMETHODIMP put_Completed(Handler* handler) override { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); handler_ = handler; return S_OK; } IFACEMETHODIMP get_Completed(Handler** handler) override { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); return handler_.CopyTo(handler); } IFACEMETHODIMP GetResults(internal::AbiT* results) override { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); return storage_ ? internal::CopyStorage(storage_, results) : E_PENDING; } ResultCallback callback() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!callback_.is_null()); return std::move(callback_); } private: void InvokeCompletedHandler() { handler_->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed); } void OnResult(ResultT result) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!storage_); storage_ = std::move(result); InvokeCompletedHandler(); } ResultCallback callback_; Microsoft::WRL::ComPtr handler_; StorageT storage_; THREAD_CHECKER(thread_checker_); base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(AsyncOperation); }; } // namespace win } // namespace base #endif // BASE_WIN_ASYNC_OPERATION_H_