Skip to content

Commit

Permalink
[base] Provide IAsyncOperation Implementation
Browse files Browse the repository at this point in the history
This change implements Windows::Foundation::IAsyncOperation. It provides
a simple interface and allows clients to specify desired results via a
callback.

This is mostly useful for tests, when custom fake implementations of
UWP interfaces are required. Some of these implementations must provide
Async methods, that return an instantiation of IAsyncOperation.

Bug: 821766
Change-Id: I728c0bb5311898fac4ee5f5e54d8a64b2aa61d94
Reviewed-on: https://chromium-review.googlesource.com/984194
Reviewed-by: Robert Liao <robliao@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551811}
  • Loading branch information
jdoerrie authored and Commit Bot committed Apr 18, 2018
1 parent f6df50c commit e4a2c85
Show file tree
Hide file tree
Showing 3 changed files with 420 additions and 0 deletions.
2 changes: 2 additions & 0 deletions base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,7 @@ jumbo_component("base") {
"version.h",
"vlog.cc",
"vlog.h",
"win/async_operation.h",
"win/com_init_check_hook.cc",
"win/com_init_check_hook.h",
"win/com_init_util.cc",
Expand Down Expand Up @@ -2352,6 +2353,7 @@ test("base_unittests") {
"values_unittest.cc",
"version_unittest.cc",
"vlog_unittest.cc",
"win/async_operation_unittest.cc",
"win/com_init_check_hook_unittest.cc",
"win/com_init_util_unittest.cc",
"win/core_winrt_util_unittest.cc",
Expand Down
244 changes: 244 additions & 0 deletions base/win/async_operation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// 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 <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/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<base::win::AsyncOperation<int>>();
// 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<base::win::AsyncOperation<FooBar*>>();
// auto cb = interface_op->callback();
// interface_op->put_Completed(...event handler...);
// ...
// // This will invoke the event handler.
// std::move(cb).Run(WRL::Make<IFooBarImpl>());
// ...
// // Results can be queried:
// WRL::ComPtr<IFooBar> results;
// interface_op->GetResults(&results);
// // |results| points to the provided IFooBarImpl instance.
//
// // Offloading a heavy computation:
// auto my_op = WRL::Make<base::win::AsyncOperation<FooBar*>>();
// 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<RCFoo*,IFoo*>"
//
// windows.foundation.collections.h defines the following template and
// semantics in Windows::Foundation::Internal:
//
// template <class LogicalType, class AbiType>
// 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 <typename T>
using ComplexT =
typename ABI::Windows::Foundation::IAsyncOperation<T>::TResult_complex;

template <typename T>
using AbiT =
typename ABI::Windows::Foundation::Internal::GetAbiType<ComplexT<T>>::type;

template <typename T>
using LogicalT = typename ABI::Windows::Foundation::Internal::GetLogicalType<
ComplexT<T>>::type;

template <typename T>
using InterfaceT = std::remove_pointer_t<AbiT<T>>;

// Implementation of shared functionality.
template <class T>
class AsyncOperationBase
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<
Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>,
ABI::Windows::Foundation::IAsyncOperation<T>> {
public:
using Handler = ABI::Windows::Foundation::IAsyncOperationCompletedHandler<T>;

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> handler_;

DISALLOW_COPY_AND_ASSIGN(AsyncOperationBase);
};

} // namespace internal

template <typename T, typename Enable = void>
class AsyncOperation;

template <typename T>
class AsyncOperation<
T,
std::enable_if_t<std::is_base_of<IUnknown, internal::InterfaceT<T>>::value>>
: public internal::AsyncOperationBase<T> {
public:
using InterfacePointer = Microsoft::WRL::ComPtr<internal::InterfaceT<T>>;
using ResultCallback = base::OnceCallback<void(InterfacePointer)>;

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<T>* 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<AsyncOperation> weak_factory_;
};

template <typename T>
class AsyncOperation<
T,
std::enable_if_t<
!std::is_base_of<IUnknown, internal::InterfaceT<T>>::value>>
: public internal::AsyncOperationBase<T> {
public:
using ResultCallback = base::OnceCallback<void(T)>;

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<T>* 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<T> value_;
base::WeakPtrFactory<AsyncOperation> weak_factory_;
};

} // namespace win
} // namespace base

#endif // BASE_WIN_ASYNC_OPERATION_H_
Loading

0 comments on commit e4a2c85

Please sign in to comment.