// 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>; // Implementation of shared functionality. template class AsyncOperationBase : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::IAsyncOperation> { public: using Handler = ABI::Windows::Foundation::IAsyncOperationCompletedHandler; AsyncOperationBase() = default; ~AsyncOperationBase() { 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); } protected: void InvokeCompletedHandler() { handler_->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed); } THREAD_CHECKER(thread_checker_); private: Microsoft::WRL::ComPtr handler_; DISALLOW_COPY_AND_ASSIGN(AsyncOperationBase); }; } // namespace internal template class AsyncOperation; template class AsyncOperation< T, std::enable_if_t>::value>> : public internal::AsyncOperationBase { public: using InterfacePointer = Microsoft::WRL::ComPtr>; 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()); } ResultCallback callback() { // Note: `this->` here and below is necessary due to the // -Wmicrosoft-template compiler warning. DCHECK_CALLED_ON_VALID_THREAD(this->thread_checker_); DCHECK(!callback_.is_null()); return std::move(callback_); } // ABI::Windows::Foundation::IAsyncOperation: IFACEMETHODIMP GetResults(internal::AbiT* results) override { DCHECK_CALLED_ON_VALID_THREAD(this->thread_checker_); return ptr_ ? ptr_.CopyTo(results) : E_PENDING; } private: void OnResult(InterfacePointer ptr) { DCHECK_CALLED_ON_VALID_THREAD(this->thread_checker_); DCHECK(!ptr_); ptr_ = std::move(ptr); this->InvokeCompletedHandler(); } ResultCallback callback_; InterfacePointer ptr_; base::WeakPtrFactory weak_factory_; }; template class AsyncOperation< T, std::enable_if_t< !std::is_base_of>::value>> : public internal::AsyncOperationBase { public: using ResultCallback = base::OnceCallback; AsyncOperation() : weak_factory_(this) { callback_ = base::BindOnce(&AsyncOperation::OnResult, weak_factory_.GetWeakPtr()); } ResultCallback callback() { DCHECK_CALLED_ON_VALID_THREAD(this->thread_checker_); DCHECK(!callback_.is_null()); return std::move(callback_); } // ABI::Windows::Foundation::IAsyncOperation: IFACEMETHODIMP GetResults(internal::AbiT* results) override { DCHECK_CALLED_ON_VALID_THREAD(this->thread_checker_); if (!value_) return E_PENDING; *results = *value_; return S_OK; } private: void OnResult(T result) { DCHECK_CALLED_ON_VALID_THREAD(this->thread_checker_); DCHECK(!value_); value_.emplace(std::move(result)); this->InvokeCompletedHandler(); } ResultCallback callback_; base::Optional value_; base::WeakPtrFactory weak_factory_; }; } // namespace win } // namespace base #endif // BASE_WIN_ASYNC_OPERATION_H_