diff options
author | Woody Chow <woodychow@google.com> | 2020-07-07 12:47:05 +0900 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-07-07 16:37:43 +0000 |
commit | d58198336b6edbb489c0a3d39ec2ac793a96afeb (patch) | |
tree | 9da9751f965ac8cdb8411a0766dad4f0676e1083 | |
parent | fe014639c72a2214d31e235654d2926e05418beb (diff) | |
download | platform_external_libbrillo-d58198336b6edbb489c0a3d39ec2ac793a96afeb.tar.gz platform_external_libbrillo-d58198336b6edbb489c0a3d39ec2ac793a96afeb.tar.bz2 platform_external_libbrillo-d58198336b6edbb489c0a3d39ec2ac793a96afeb.zip |
Move libbrillo::Future to vm_tools/concierge
As it is still experimental and unpolished
BUG=chromium:1102226
TEST=cros_run_unit_tests --board $BOARD --packages \
"libbrillo vm_host_tools"
Change-Id: If16cd8ed6b9207349958f526ba55bda0dad20562
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2284324
Tested-by: Woody Chow <woodychow@chromium.org>
Reviewed-by: Chirantan Ekbote <chirantan@chromium.org>
Reviewed-by: Hidehiko Abe <hidehiko@chromium.org>
Commit-Queue: Woody Chow <woodychow@chromium.org>
Cr-Mirrored-From: https://chromium.googlesource.com/chromiumos/platform2
Cr-Mirrored-Commit: aec2eaa3b24aa7fc9ec3c27a8675265cb67837c7
-rw-r--r-- | BUILD.gn | 1 | ||||
-rw-r--r-- | brillo/apply_impl.h | 34 | ||||
-rw-r--r-- | brillo/future.h | 644 | ||||
-rw-r--r-- | brillo/future_test.cc | 478 |
4 files changed, 0 insertions, 1157 deletions
@@ -544,7 +544,6 @@ if (use.test) { "brillo/files/file_util_test.cc", "brillo/files/safe_fd_test.cc", "brillo/flag_helper_test.cc", - "brillo/future_test.cc", "brillo/glib/object_test.cc", "brillo/http/http_connection_curl_test.cc", "brillo/http/http_form_data_test.cc", diff --git a/brillo/apply_impl.h b/brillo/apply_impl.h deleted file mode 100644 index f1acef1..0000000 --- a/brillo/apply_impl.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2020 The Chromium OS 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 LIBBRILLO_BRILLO_APPLY_IMPL_H_ -#define LIBBRILLO_BRILLO_APPLY_IMPL_H_ - -#include <utility> - -namespace brillo { -namespace internal { - -template <typename U, typename ... Ts, class Tuple, std::size_t... I> -U ApplyImpl(base::OnceCallback<U(Ts...)> f, Tuple&& t, - std::index_sequence<I...>) { - return std::move(f).Run(std::move(std::get<I>(std::forward<Tuple>(t)))...); -} - -template <typename U, typename ... Ts, class Tuple> -U Apply(base::OnceCallback<U(Ts...)> f, Tuple&& t) { - return ApplyImpl( - std::move(f), std::forward<Tuple>(t), - std::make_index_sequence<sizeof...(Ts)>{}); -} - -template <typename U, typename T> -U Apply(base::OnceCallback<U(T)> f, T&& val) { - return std::move(f).Run(std::forward<T>(val)); -} - -} // namespace internal -} // namespace brillo - -#endif // LIBBRILLO_BRILLO_APPLY_IMPL_H_ diff --git a/brillo/future.h b/brillo/future.h deleted file mode 100644 index 122831a..0000000 --- a/brillo/future.h +++ /dev/null @@ -1,644 +0,0 @@ -// Copyright 2020 The Chromium OS 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 LIBBRILLO_BRILLO_FUTURE_H_ -#define LIBBRILLO_BRILLO_FUTURE_H_ - -#include <atomic> -#include <memory> -#include <tuple> -#include <utility> -#include <vector> - -#include <base/bind.h> -#include <base/optional.h> -#include <base/run_loop.h> -#include <base/task_runner.h> -#include <base/threading/sequenced_task_runner_handle.h> - -#include <brillo/apply_impl.h> - -// A future class and utilities adapted to the Chrome OS code base. It can be -// used to post jobs to the same/different threads, and to add async support -// to methods. Please refer to FutureTest_Tutorial of future_test.cc on how to -// use this class. -// -// * Regarding "NoReject" (|Then| vs |ThenNoReject|, -// and |Async| vs |AsyncNoReject|) -// -// Use the NoReject variant if there is no need to reject a promise in your -// function. It allows you to return the output directly without using -// `return brillo::Resolve<T>(val);`, which reduces the amount of boilerplate -// code. -// -// The non "NoReject" func gives the freedom to use both |Resolve| and |Reject|. -// -// * Support of std::tuple and std::array -// -// If the value type of the Future is either std::tuple or std::array, the value -// is unpacked automatically for the next |Then| func. See FutureTest_Tuple -// and FutureTest_Array for example. -namespace brillo { - -template <typename T, typename Error> class Promise; - -// A struct to store both the value and the error for |Future::Get|. -// In a common C++ implementation of futures, this is not need as the exception -// is thrown if any when |Get| is called. -template <typename T, typename Error = void, - class Enable1 = void, class Enable2 = void> struct GetResult; - -template <typename T, typename Error> -struct GetResult< - T, Error, - typename std::enable_if_t<!std::is_void<T>::value>, - typename std::enable_if_t<!std::is_void<Error>::value>> { - T val; - Error err; - bool rejected = false; -}; - -template <typename T, typename Error> -struct GetResult< - T, Error, - typename std::enable_if_t<std::is_void<T>::value>, - typename std::enable_if_t<!std::is_void<Error>::value>> { - Error err; - bool rejected = false; -}; - -template <typename T, typename Error> -struct GetResult< - T, Error, - typename std::enable_if_t<!std::is_void<T>::value>, - typename std::enable_if_t<std::is_void<Error>::value>> { - T val; - bool rejected = false; -}; - -template <typename T, typename Error> -struct GetResult< - T, Error, - typename std::enable_if_t<std::is_void<T>::value>, - typename std::enable_if_t<std::is_void<Error>::value>> { - bool rejected = false; -}; - -template <typename T, typename Error = void> -static typename std::enable_if_t<!std::is_void<T>::value, GetResult<T, Error>> -Resolve(T val) { - GetResult<T, Error> ret; - ret.val = std::move(val); - ret.rejected = false; - return ret; -} - -template <typename T, typename Error = void> -static typename std::enable_if_t<std::is_void<T>::value, GetResult<T, Error>> -Resolve() { - GetResult<void, Error> ret; - ret.rejected = false; - return ret; -} - -template <typename T, typename Error = void> -typename std::enable_if_t<!std::is_void<Error>::value, GetResult<T, Error>> -Reject(Error err) { - GetResult<T, Error> ret; - ret.err = std::move(err); - ret.rejected = true; - return ret; -} - -template <typename T, typename Error = void> -typename std::enable_if_t<std::is_void<Error>::value, GetResult<T, void>> -Reject() { - GetResult<T, void> ret; - ret.rejected = true; - return ret; -} - -namespace internal { - -template <typename T, typename Error> -struct SharedState { - mutable base::Lock mutex; - GetResult<T, Error> ret; - bool done = false; - scoped_refptr<base::TaskRunner> task_runner; - base::OnceClosure then_func; -}; - -}; // namespace internal - -// Error: User defined error type used in |Reject| and |OnReject|. -template <typename T, typename Error = void> -class Future { - public: - Future() = default; - explicit Future(std::shared_ptr<internal::SharedState<T, Error>> state) : - state_(std::move(state)) {} - Future(Future&&) = default; - Future& operator=(Future&&) = default; - - // |func| will be posted to the task_runner when this future is fulfilled by - // returning |brillo::Resolve<T>(val)| or |brillo::Reject<T>(err)|. - // - // Returns the future of the posted func. Task_runner of this future class is - // inherited by the returned future. - // - // |OnReject| can be used to handle the rejection if there was a reject. If - // the rejection is not handled, the subsequent Then funcs will be bypassed - // and rejection signal will be propagated. - template <typename T_then, typename ... Ts> - Future<T_then, Error> Then( - base::OnceCallback<GetResult<T_then, Error>(Ts...)> func); - template <typename T_then, typename ... Ts> - Future<T_then, Error> ThenNoReject(base::OnceCallback<T_then(Ts...)> func); - - // |func| is triggered if |brillo::Reject<T>(err)| is returned by previous - // |Then| func. The chain can be resumed by returning - // |brillo::Resolve<T>(val)|, or keep skipping by returning - // |brillo::Reject<T>(err)| within |func|. - // - // Returns the future of the posted func. task_runner of this future class is - // inherited by the returned future. - template<typename ... ErrorOrVoid> - Future<T, Error> OnReject( - base::OnceCallback<GetResult<T, Error>(ErrorOrVoid...)> func); - - // Use a RunLoop to wait for the promise to be fulfilled, return - // the result and reset the shared state. In other words, the future becomes - // invalid after Get() returns. - GetResult<T, Error> - Get(base::RunLoop::Type type = base::RunLoop::Type::kNestableTasksAllowed); - - // TODO(woodychow): Implement a wait function with timeout - - // Update the |task_runner|. |Then|/|OnReject| functions will be posted to - // this task_runner - Future<T, Error> Via(scoped_refptr<base::TaskRunner> task_runner) { - DCHECK(task_runner); - base::AutoLock guard(state_->mutex); - state_->task_runner = task_runner; - return std::move(*this); - } - - // Returns true if the promise has been fulfilled. False otherwise. - bool IsDone() const { - base::AutoLock guard(state_->mutex); - return state_->done; - } - - private: - // This function should only be called in the internal Then wrapper, where the - // promise is fulfilled and the val is guaranteed to be there. - // Wait, this assumes the original func and the Then func are executed in the - // same sequence - GetResult<T, Error> GetHelper(); - - template<typename U = T, typename UError = Error> - static typename std::enable_if_t<std::is_void<UError>::value> - RejectFuncHelper(base::OnceCallback<GetResult<T, void>()> reject_func, - Promise<T, void> p, - GetResult<T, void> ret) { - p.SetResult(std::move(reject_func).Run()); - } - template<typename U = T, typename UError = Error> - static typename std::enable_if_t<!std::is_void<UError>::value> - RejectFuncHelper(base::OnceCallback<GetResult<T, UError>(UError)> reject_func, - Promise<T, UError> p, - GetResult<T, UError> ret) { - p.SetResult(std::move(reject_func).Run(std::move(ret.err))); - } - - template<typename T_then, typename UError = Error> - static typename std::enable_if_t<std::is_void<UError>::value> - ResolveFuncHelper(base::OnceCallback<GetResult<T_then, Error>()> resolve_func, - Promise<T_then, Error> p, - GetResult<T, Error> ret) { - p.SetResult(std::move(resolve_func).Run()); - } - - template<typename T_then, typename ... Ts, typename UError = Error> - static typename std::enable_if_t<std::is_void<UError>::value> - ResolveFuncHelper( - base::OnceCallback<GetResult<T_then, Error>(Ts...)> resolve_func, - Promise<T_then, Error> p, - GetResult<T, Error> ret) { - p.SetResult(internal::Apply(std::move(resolve_func), std::move(ret.val))); - } - - template <typename T_then, typename UError = Error> - static typename std::enable_if_t<std::is_void<UError>::value> - PropagateReject(Promise<T_then, void> p, GetResult<T, void> ret) { - p.SetResult(Reject<T_then>()); - } - - template <typename T_then, typename UError = Error> - static typename std::enable_if_t<!std::is_void<UError>::value> - PropagateReject(Promise<T_then, UError> p, GetResult<T, UError> ret) { - p.SetResult(Reject<T_then, Error>(ret.err)); - } - - template <typename T_then, typename ... Ts> - Future<T_then, Error> ThenHelper( - scoped_refptr<base::TaskRunner> task_runner, - base::OnceCallback<void(Promise<T_then, Error>, GetResult<T, Error>)> - resolve_func, - base::OnceCallback<void(Promise<T_then, Error>, GetResult<T, Error>)> - reject_func); - - std::shared_ptr<internal::SharedState<T, Error>> state_; - DISALLOW_COPY_AND_ASSIGN(Future); -}; - -template <typename T, typename Error = void> -class Promise { - public: - Promise() { state_ = std::make_shared<internal::SharedState<T, Error>>(); } - explicit Promise(std::shared_ptr<internal::SharedState<T, Error>> state) : - state_(std::move(state)) {} - Promise(Promise&&) = default; - Promise& operator=(Promise&&) = default; - - // Returns a future that can be used to wait for this promise to be fulfilled. - Future<T, Error> GetFuture(scoped_refptr<base::TaskRunner> task_runner) { - base::AutoLock guard(state_->mutex); - state_->task_runner = task_runner; - return Future<T, Error>(state_); - } - - // Fulfill this promise. The shared state will be released upon this. - template<typename U = T> - typename std::enable_if_t<std::is_void<U>::value> SetValue(); - template<typename U = T> - typename std::enable_if_t<!std::is_void<U>::value> SetValue(U val); - - // Reject this promise. The shared state will be released upon this. - template<typename UError = Error> - typename std::enable_if_t<std::is_void<UError>::value> Reject(); - template<typename UError = Error> - typename std::enable_if_t<!std::is_void<UError>::value> Reject(UError err); - - void SetResult(GetResult<T, Error> ret); - - private: - // Lock state_.mutex before calling this - void SetValueHelper(); - - std::shared_ptr<internal::SharedState<T, Error>> state_; - DISALLOW_COPY_AND_ASSIGN(Promise); -}; - -// ------ Promise impl ------ - -template <typename T, typename Error> -void Promise<T, Error>::SetValueHelper() { - DCHECK(!state_->done); - state_->done = true; - - // Handle "then" - if (!state_->then_func.is_null()) { - std::move(state_->then_func).Run();; - } -} - -template <typename T, typename Error> -template <typename U> -typename std::enable_if_t<std::is_void<U>::value> -Promise<T, Error>::SetValue() { - base::AutoLock guard(state_->mutex); - state_->ret = brillo::Resolve<void, Error>(); - SetValueHelper(); -} - -template <typename T, typename Error> -template <typename U> -typename std::enable_if_t<!std::is_void<U>::value> -Promise<T, Error>::SetValue(U val) { - base::AutoLock guard(state_->mutex); - state_->ret = brillo::Resolve<T, Error>(std::move(val)); - SetValueHelper(); -} - -template <typename T, typename Error> -template <typename UError> -typename std::enable_if_t<std::is_void<UError>::value> -Promise<T, Error>::Reject() { - base::AutoLock guard(state_->mutex); - state_->ret = brillo::Reject<T, void>(); - state_->done = true; - - if (!state_->then_func.is_null()) { - std::move(state_->then_func).Run(); - } -} - -template <typename T, typename Error> -template <typename UError> -typename std::enable_if_t<!std::is_void<UError>::value> -Promise<T, Error>::Reject(UError err) { - base::AutoLock guard(state_->mutex); - state_->ret = brillo::Reject<T, Error>(std::move(err)); - state_->done = true; - - if (!state_->then_func.is_null()) { - std::move(state_->then_func).Run(); - } -} - -template <typename T, typename Error> -void Promise<T, Error>::SetResult(GetResult<T, Error> ret) { - base::AutoLock guard(state_->mutex); - state_->ret = std::move(ret); - SetValueHelper(); -} - -// ------ Future impl ------ - -template <typename T, typename Error> -template <typename T_then, typename ... Ts> -Future<T_then, Error> Future<T, Error>::ThenHelper( - scoped_refptr<base::TaskRunner> task_runner, - base::OnceCallback<void(Promise<T_then, Error>, - GetResult<T, Error>)> resolve_func, - base::OnceCallback<void(Promise<T_then, Error>, - GetResult<T, Error>)> reject_func) { - base::AutoLock guard(state_->mutex); - if (task_runner) { - state_->task_runner = task_runner; - } - Promise<T_then, Error> promise; - Future<T_then, Error> future = promise.GetFuture(state_->task_runner); - internal::SharedState<T, Error>* pState = state_.get(); - base::OnceCallback<void(Future<T, Error>, Promise<T_then, Error>)> - wrapped_func = base::BindOnce([]( - base::OnceCallback<void(Promise<T_then, Error>, - GetResult<T, Error>)> resolve_func, - base::OnceCallback<void(Promise<T_then, Error>, - GetResult<T, Error>)> reject_func, - Future<T, Error> old_future, - Promise<T_then, Error> p) { - GetResult<T, Error> ret = old_future.Get(); - if (ret.rejected) { - std::move(reject_func).Run(std::move(p), std::move(ret)); - } else { - std::move(resolve_func).Run(std::move(p), std::move(ret)); - } - }, std::move(resolve_func), std::move(reject_func)); - - base::OnceClosure post_func = base::BindOnce([]( - scoped_refptr<base::TaskRunner> task_runner, - base::OnceClosure func) { - CHECK(task_runner); - task_runner->PostTask(FROM_HERE, std::move(func)); - }, state_->task_runner, base::BindOnce(std::move(wrapped_func), - std::move(*this), std::move(promise))); - if (pState->done) { - // post immediately - std::move(post_func).Run(); - } else { - // Promise::SetValue/Reject will run this func - pState->then_func = std::move(post_func); - } - return future; -} - -template <typename T, typename Error> -template <typename T_then, typename ... Ts> -Future<T_then, Error> Future<T, Error>::Then( - base::OnceCallback<GetResult<T_then, Error>(Ts...)> func) { - return ThenHelper<T_then, Ts...>( - nullptr, - base::BindOnce([]( - base::OnceCallback<GetResult<T_then, Error>(Ts...)> resolve_func, - Promise<T_then, Error> p, - GetResult<T, Error> ret) { - ResolveFuncHelper(std::move(resolve_func), std::move(p), std::move(ret)); - }, std::move(func)), - base::BindOnce(&Future<T, Error>::PropagateReject<T_then>)); -} - -namespace internal { - -template <typename Error, typename T_then, typename ... Ts> -typename std::enable_if_t<!std::is_void<T_then>::value, - base::OnceCallback<GetResult<T_then, Error>(Ts...)>> -FutureBind(base::OnceCallback<T_then(Ts...)> func) { - return base::BindOnce([](base::OnceCallback<T_then(Ts...)> func, Ts... args) { - return Resolve<T_then, Error>(std::move(func).Run(std::move(args)...)); - }, std::move(func)); -} - -template <typename Error, typename T_then, typename ... Ts> -typename std::enable_if_t<std::is_void<T_then>::value, - base::OnceCallback<GetResult<T_then, Error>(Ts...)>> -FutureBind(base::OnceCallback<T_then(Ts...)> func) { - return base::BindOnce([](base::OnceCallback<void(Ts...)> func, Ts... args) { - std::move(func).Run(std::move(args)...); - return Resolve<void, Error>(); - }, std::move(func)); -} - -}; // namespace internal - -template <typename T, typename Error> -template <typename T_then, typename ... Ts> -Future<T_then, Error> Future<T, Error>::ThenNoReject( - base::OnceCallback<T_then(Ts...)> func) { - return Then(internal::FutureBind<Error>(std::move(func))); -} - -template <typename T, typename Error> -template<typename ... ErrorOrVoid> -Future<T, Error> Future<T, Error>::OnReject( - base::OnceCallback<GetResult<T, Error>(ErrorOrVoid...)> func) { - return ThenHelper<T, T>( - nullptr, - base::BindOnce([](Promise<T, Error> p, GetResult<T, Error> ret) { - p.SetResult(std::move(ret)); - }), - base::BindOnce([]( - base::OnceCallback<GetResult<T, Error>(ErrorOrVoid...)> func, - Promise<T, Error> p, - GetResult<T, Error> ret) { - Future<T, Error>::RejectFuncHelper(std::move(func), std::move(p), - std::move(ret)); - }, std::move(func))); -} - -template <typename T, typename Error> -GetResult<T, Error> Future<T, Error>::Get(base::RunLoop::Type type) { - if (!IsDone()) { - base::RunLoop loop(type); - { - base::AutoLock guard(state_->mutex); - DCHECK(state_->then_func.is_null()); - state_->then_func = loop.QuitClosure(); - } - loop.Run(); - } - return GetHelper(); -} - -template <typename T, typename Error> -GetResult<T, Error> Future<T, Error>::GetHelper() { - GetResult<T, Error> ret; - { - base::AutoLock guard(state_->mutex); - DCHECK(state_->done); - ret = std::move(state_->ret); - } - state_.reset(); - return ret; -} - -/* ------ Non class method declarations ------*/ - -// Flatten a nested future. Useful when making an async call inside an async -// function. -// -// TODO(woodychow): Make this a member function of Future with some template -// magic -template <typename T, typename Error> -Future<T, Error> Flatten(Future<Future<T, Error>, Error> f); - -// Post |func| to |task_runner|, and return a future that will be ready upon -// completion of the posted |func| -template <typename T, typename Error = void> -Future<T, Error> Async(scoped_refptr<base::TaskRunner> task_runner, - base::OnceCallback<GetResult<T, Error>()> func); -template <typename T, typename Error = void> -Future<T, Error> AsyncNoReject(scoped_refptr<base::TaskRunner> task_runner, - base::OnceCallback<T()> func); - -// Returns a future that will be ready when all the given futures are ready -// If any of the given futures is rejected, the returned future will be rejected -// as well -template <typename T, typename Error> -Future<std::vector<T>, Error> Collect( - scoped_refptr<base::TaskRunner> task_runner, - std::vector<Future<T, Error>> futures); - -/* ------ Non class method implementation ------ */ - -template <typename T, typename Error> -Future<T, Error> Flatten(Future<Future<T, Error>, Error> f) { - return f.Then(base::BindOnce([](Future<T, Error> f) { - return f.Get(); - })); -} - -template <typename T, typename Error> -Future<T, Error> Async(scoped_refptr<base::TaskRunner> task_runner, - base::OnceCallback<GetResult<T, Error>()> func) { - Promise<T, Error> p; - Future<T, Error> future = p.GetFuture(task_runner); - task_runner->PostTask(FROM_HERE, base::BindOnce([]( - Promise<T, Error> p, - base::OnceCallback<GetResult<T, Error>()> func) { - p.SetResult(std::move(func).Run()); - }, std::move(p), std::move(func))); - return future; -} - -template <typename T, typename Error> -Future<T, Error> AsyncNoReject(scoped_refptr<base::TaskRunner> task_runner, - base::OnceCallback<T()> func) { - Promise<T, Error> p; - Future<T, Error> future = p.GetFuture(task_runner); - task_runner->PostTask(FROM_HERE, base::BindOnce([]( - Promise<T, Error> p, - base::OnceCallback<GetResult<T, Error>()> func) { - p.SetResult(std::move(func).Run()); - }, std::move(p), internal::FutureBind<Error>(std::move(func)))); - return future; -} - -template <typename T, typename Error> -Future<std::vector<T>, Error> Collect( - scoped_refptr<base::TaskRunner> task_runner, - std::vector<Future<T, Error>> futures) { - struct Context { - explicit Context(size_t n) { - values.resize(n); - } - void Reject(Error e) { - if (!rejected.fetch_or(1)) { // only reject once - promise.Reject(std::move(e)); - } - } - ~Context() { - if (!rejected.load()) { - promise.SetValue(std::move(values)); - } - } - std::atomic<uint8_t> rejected { 0 }; - Promise<std::vector<T>> promise; - std::vector<T> values; - }; - - std::shared_ptr<Context> ctx = std::make_shared<Context>(futures.size()); - - for (size_t i = 0; i < futures.size(); ++i) { - futures[i].Via(task_runner).Then(base::BindOnce([]( - std::shared_ptr<Context> ctx, size_t i, T val) { - ctx->values[i] = std::move(val); - return Resolve<void>(); - }, ctx, i)).OnReject(base::BindOnce([]( - std::shared_ptr<Context> ctx, Error e) { - ctx->Reject(std::move(e)); - // Whatever as the future returned by this OnReject is not used - return Resolve<void>(); - }, ctx)); - } - - return ctx->promise.GetFuture(task_runner); -} - -template <typename T> -Future<std::vector<T>, void> Collect( - scoped_refptr<base::TaskRunner> task_runner, - std::vector<Future<T, void>> futures) { - struct Context { - explicit Context(size_t n) { - values.resize(n); - } - void Reject() { - if (!rejected.fetch_or(1)) { // only reject once - promise.Reject(); - } - } - ~Context() { - if (!rejected.load()) { - promise.SetValue(std::move(values)); - } - } - std::atomic<uint8_t> rejected { 0 }; - Promise<std::vector<T>> promise; - std::vector<T> values; - }; - - std::shared_ptr<Context> ctx = std::make_shared<Context>(futures.size()); - - for (size_t i = 0; i < futures.size(); ++i) { - futures[i].Via(task_runner).Then(base::BindOnce([]( - std::shared_ptr<Context> ctx, size_t i, T val) { - ctx->values[i] = std::move(val); - return Resolve<void>(); - }, ctx, i)).OnReject(base::BindOnce([]( - std::shared_ptr<Context> ctx) { - ctx->Reject(); - // Whatever as the future returned by this OnReject is not used - return Resolve<void>(); - }, ctx)); - } - - return ctx->promise.GetFuture(task_runner); -} - -} // namespace brillo - -#endif // LIBBRILLO_BRILLO_FUTURE_H_ diff --git a/brillo/future_test.cc b/brillo/future_test.cc deleted file mode 100644 index 29ae472..0000000 --- a/brillo/future_test.cc +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright 2020 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <array> -#include <base/test/task_environment.h> -#include <base/threading/sequenced_task_runner_handle.h> -#include <base/threading/platform_thread.h> -#include <base/threading/thread.h> -#include <brillo/future.h> -#include <gtest/gtest.h> -#include <string> - -#include <base/test/test_timeouts.h> - -namespace brillo { - -class FutureTest : public ::testing::Test { - public: - static void SetUpTestSuite() { TestTimeouts::Initialize(); } - - private: - void SetUp() override { - ASSERT_TRUE(worker_thread_.Start()); - task_runner_ = worker_thread_.task_runner(); - } - void TearDown() override { - task_runner_ = nullptr; - worker_thread_.Stop(); - } - - protected: - // Both single and multi threaded usage of future is tested - base::test::TaskEnvironment task_environment_; - base::Thread worker_thread_{"worker thread"}; - scoped_refptr<base::SequencedTaskRunner> task_runner_; -}; - -// The comments below assume basic knowledge of the promise/future programming -// model. -TEST_F(FutureTest, Tutorial) { - { - // Dispatching a basic function to |task_runner_|, and returns a future to - // wait on - auto sum = Async<int>( - task_runner_, // Either the current or a different thread would work - base::BindOnce([](int x, int y) { - return Resolve<int>(x + y); - }, 4, 3)); - // Blocking wait on the future returned by the |Async| function. - // A |RunLoop| with |kNestableTasksAllowed| is used in Get() to prevent - // deadlock while waiting for the task to finish on the same thread. - EXPECT_EQ(sum.Get().val, 7); - } - - { - auto sum = AsyncNoReject<int>( - task_runner_, - base::BindOnce([](int x, int y) { - return x + y; // Use AsyncNoReject here to avoid boiletplate code - }, 1, 2)) - // Chain another async function. The return of the previous func |x + y| - // needs to match the type of |z|. - // - // The Then func will be posted to the same task_runner by default. Use - // |future.Via(another_task_runner)| to change the task_runner in the middle - // of a chain if needed. - .ThenNoReject(base::BindOnce([](int z) { - return z + 5; - })); - EXPECT_EQ(sum.Get().val, 8); - } - - { - Future<int> future = Async(task_runner_, base::BindOnce([](){ - // Reject, bypass any |Then| after here and before the first |OnReject| - return Reject<int>(); - })) - .Then(base::BindOnce([](int x){ - // no-op, as any |Then| func after a reject is bypassed - return Resolve<int>(1); - })) - // Here, the default error type is void. If a user defined error type is - // used, the reject handling func will take an Error arg. - // See FutureTest - RejectTypes for an example of that - .OnReject(base::BindOnce([](){ - // Can either |Resolve| or |Reject| here. - return Resolve<int>(2); - })); - GetResult<int> ret = future.Get(); - EXPECT_EQ(ret.val, 2); - EXPECT_FALSE(ret.rejected); // handled, therefore false - } -} - -TEST_F(FutureTest, VoidType) { - { - Async(task_runner_, base::BindOnce([](){ - return Resolve<void>(); - })).Get(); - } -} - -TEST_F(FutureTest, Chaining) { - { - auto future = Async(task_runner_, base::BindOnce([]( - int x, int y) { - return Resolve<int>(x + y); - }, 4, 3)) - .Then(base::BindOnce([](int z) { - return Resolve<int>(z * 2); - })); - EXPECT_EQ(future.Get().val, 14); - } - - { - auto future = Async(task_runner_, base::BindOnce([](int x, int y) { - return Resolve<int>(x + y); - }, 4, 3)) - .Then(base::BindOnce([](int z) { - return Resolve<int>(z * 2); - })) - .Then(base::BindOnce([](int z) { - return Resolve<int>(z * 3); - })); - EXPECT_EQ(future.Get().val, 42); - } -} - -TEST_F(FutureTest, Reject) { - // Reject, Future<void> - { - int x = 1; - Future<void> future = Async(task_runner_, base::BindOnce([](){ - return Reject<void>(); - })) - .ThenNoReject(base::BindOnce([](int* x){ - *x *= 2; // no-op, as Reject above should break the then chain - }, base::Unretained(&x))) - .OnReject(base::BindOnce([](int* x){ - *x *= 3; - return Resolve<void>(); - }, base::Unretained(&x))); - - EXPECT_FALSE(future.Get().rejected); - EXPECT_EQ(x, 3); - } - - // Reject again, Future<int> - { - Future<int> future = Async(task_runner_, base::BindOnce([](){ - return Reject<int>(); - })) - .Then(base::BindOnce([](int x){ - // no-op, as Reject above should break the then chain - return Resolve<int>(1); - })) - .OnReject(base::BindOnce([](){ - return Reject<int>(); - })); - EXPECT_TRUE(future.Get().rejected); - } - - // Resolve, Future<int> - { - Future<int> future = Async(task_runner_, base::BindOnce([](){ - return Resolve<int>(1); - })).Then(base::BindOnce([](int x){ - return Resolve<int>(x * 2); - })).OnReject(base::BindOnce([]() { - return Resolve<int>(3); // should be no-op as no promise is rejected - })); - GetResult<int> ret = future.Get(); - EXPECT_FALSE(ret.rejected); - EXPECT_EQ(ret.val, 2); - } -} - -TEST_F(FutureTest, RejectTypes) { - { - Future<int, std::string> future = Async(task_runner_, - base::BindOnce([]() { - return Reject<int, std::string>("a"); - })).OnReject(base::BindOnce([](std::string err){ - if (err == "a") { - return Resolve<int, std::string>(5); - } else { - return Reject<int, std::string>("noooo"); - } - })); - GetResult<int, std::string> ret = future.Get(); - EXPECT_FALSE(ret.rejected); - EXPECT_EQ(ret.val, 5); - } - - { - Future<int, std::string> future = Async(task_runner_, - base::BindOnce([]() { - return Reject<int, std::string>("b"); - })).OnReject(base::BindOnce([](std::string err){ - if (err == "a") { - return Resolve<int, std::string>(5); - } else { - return Reject<int, std::string>("noooo"); - } - })); - GetResult<int, std::string> ret = future.Get(); - EXPECT_TRUE(ret.rejected); - EXPECT_EQ(ret.err, "noooo"); - } -} - -TEST_F(FutureTest, Tuple) { - { - auto future = Async( - task_runner_, - base::BindOnce([]() { - return Resolve<std::tuple<int, int8_t, int16_t, int32_t, int64_t>>( - {1, 2, 3, 4, 5}); - })).Then( - // tuple is automatically unpacked - base::BindOnce([](int a, int b, int c, int d, int e) { - return Resolve<int>(a + b + c + d + e); - })); - EXPECT_EQ(future.Get().val, 15); - } - - { - auto future = Async( - task_runner_, - base::BindOnce([]() { - return Resolve<std::tuple<int, int8_t, int16_t, int32_t, int64_t>>( - {1, 2, 3, 4, 5}); - })).ThenNoReject( - // Taking a std::tuple without unpacking should still work - base::BindOnce([](std::tuple<int, int8_t, int16_t, int32_t, int64_t> t) { - return std::get<0>(t) + std::get<1>(t) + std::get<2>(t) + - std::get<3>(t) + std::get<4>(t); - })); - EXPECT_EQ(future.Get().val, 15); - } -} - -TEST_F(FutureTest, Array) { - { - auto future = Async( - task_runner_, - base::BindOnce([](){ - return Resolve<std::array<int, 5>>({1, 2, 3, 4, 5}); - // std::array is automatically unpacked - })).Then(base::BindOnce([](int a, int b, int c, int d, int e) { - return Resolve<int>(a * b * c * d * e); - })); - - EXPECT_EQ(future.Get().val, 120); - } - - { - auto future = Async( - task_runner_, - base::BindOnce([](){ - return Resolve<std::array<int, 5>>({1, 2, 3, 4, 5}); - })).Then(base::BindOnce([](std::array<int, 5> a) { - // Taking a std::array without unpacking should still work - return Resolve<int>(a[0] * a[1] * a[2] * a[3] * a[4]); - })); - - EXPECT_EQ(future.Get().val, 120); - } -} - -TEST_F(FutureTest, Collect) { - // Different threads - { - std::vector<Future<int>> futures; - - constexpr int n = 10; - for (int i = 0; i < n; ++i) { - futures.push_back(Async( - task_runner_, - base::BindOnce([](int x) { return Resolve<int>(x); }, i))); - } - - Future<std::vector<int>> future = Collect(task_runner_, std::move(futures)); - GetResult<std::vector<int>> ret = future.Get(); - for (int i = 0; i < n; ++i) { - EXPECT_EQ(ret.val[i], i); - } - EXPECT_FALSE(ret.rejected); - } - - // Same thread - { - std::vector<Future<int>> futures; - - constexpr int n = 10; - for (int i = 0; i < n; ++i) { - futures.push_back(Async( - base::SequencedTaskRunnerHandle::Get(), - base::BindOnce([](int x) { return Resolve<int>(x); }, i))); - } - - Future<std::vector<int>> future = Collect( - base::SequencedTaskRunnerHandle::Get(), std::move(futures)); - GetResult<std::vector<int>> ret = future.Get(); - for (int i = 0; i < n; ++i) { - EXPECT_EQ(ret.val[i], i); - } - EXPECT_FALSE(ret.rejected); - } - - // Same thread, rejected - { - std::vector<Future<int>> futures; - - constexpr int n = 10; - for (int i = 0; i < n; ++i) { - futures.push_back(Async( - base::SequencedTaskRunnerHandle::Get(), - base::BindOnce([](int x) { - if (x == 7) { - return Reject<int>(); - } else { - return Resolve<int>(x); - } - }, i))); - } - - Future<std::vector<int>> future = Collect( - base::SequencedTaskRunnerHandle::Get(), std::move(futures)); - GetResult<std::vector<int>> ret = future.Get(); - EXPECT_TRUE(ret.rejected); - } -} - -TEST_F(FutureTest, Flatten) { - // Same thread - { - auto ret = Flatten(AsyncNoReject(base::SequencedTaskRunnerHandle::Get(), - base::BindOnce([](){ return 2; })) - .ThenNoReject(base::BindOnce([](int x) { - return AsyncNoReject(base::SequencedTaskRunnerHandle::Get(), - base::BindOnce([](int x){ return x * 3; }, x)); - }))).Get(); - EXPECT_EQ(ret.val, 6); - EXPECT_FALSE(ret.rejected); - } - - { - auto ret = Flatten(AsyncNoReject(base::SequencedTaskRunnerHandle::Get(), - base::BindOnce([](){ return 2; })) - .ThenNoReject(base::BindOnce([](int x) { - return Async(base::SequencedTaskRunnerHandle::Get(), - base::BindOnce([](int x){ - return Reject<int>(); - }, x)); - }))).Get(); - EXPECT_TRUE(ret.rejected); - } -} - -TEST_F(FutureTest, NoDeadlock) { - // Fulfill promise in another thread - { - Promise<bool> promise; - Future<bool> future = promise.GetFuture( - base::SequencedTaskRunnerHandle::Get()); - task_runner_->PostDelayedTask( - FROM_HERE, base::BindOnce([](Promise<bool> promise) { - promise.SetValue(true); - }, std::move(promise)), - base::TimeDelta::FromMilliseconds(10)); - EXPECT_TRUE(future.Get().val); - } - - // Nested run loops - { - base::RunLoop loop; - base::SequencedTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce([]( - base::Closure closure) { - Promise<bool> promise; - Future<bool> future = promise.GetFuture( - base::SequencedTaskRunnerHandle::Get()); - base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::BindOnce([](Promise<bool> promise) { - promise.SetValue(true); - }, std::move(promise)), base::TimeDelta::FromMilliseconds(10)); - EXPECT_TRUE(future.Get().val); - closure.Run(); - }, loop.QuitClosure())); - loop.Run(); - } - - { - Promise<bool> promise; - Future<bool> future = promise.GetFuture( - base::SequencedTaskRunnerHandle::Get()); - task_runner_->PostDelayedTask( - FROM_HERE, base::BindOnce([]( - Promise<bool> promise, - scoped_refptr<base::SequencedTaskRunner> main_thread_runner) { - main_thread_runner->PostTask(FROM_HERE, base::BindOnce([]( - Promise<bool> promise) { - promise.SetValue(true); - }, std::move(promise))); - }, std::move(promise), base::SequencedTaskRunnerHandle::Get()), - base::TimeDelta::FromMilliseconds(10)); - EXPECT_TRUE(future.Get().val); - } - - { - Promise<bool> promise; - Future<bool> future = promise.GetFuture( - base::SequencedTaskRunnerHandle::Get()); - task_runner_->PostTask( - FROM_HERE, base::BindOnce([]( - Promise<bool> promise, - scoped_refptr<base::SequencedTaskRunner> main_thread_runner) { - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); - main_thread_runner->PostTask(FROM_HERE, base::BindOnce([]( - Promise<bool> promise) { - promise.SetValue(true); - }, std::move(promise))); - }, std::move(promise), base::SequencedTaskRunnerHandle::Get())); - EXPECT_TRUE(future.Get().val); - } -} - -TEST_F(FutureTest, SameThread) { - { - auto sum = Async( - base::SequencedTaskRunnerHandle::Get(), - base::BindOnce([](int x, int y) { - return Resolve<int>(x + y); - }, 4, 3)); - EXPECT_EQ(sum.Get().val, 7); - } - - { - auto future = Async( - base::SequencedTaskRunnerHandle::Get(), - base::BindOnce([](){ return Resolve<void>(); })); - future.Get(); - } - - { - Promise<bool> promise; - Future<bool> future = promise.GetFuture( - base::SequencedTaskRunnerHandle::Get()); - promise.SetValue(true); - EXPECT_EQ(future.Get().val, true); - } - - { - auto func = [](){ - Promise<bool> promise; - Future<bool> future = promise.GetFuture( - base::SequencedTaskRunnerHandle::Get()); - promise.SetValue(true); - return future; - }; - func().Get(); - } - - { - Promise<void> promise; - Future<void> future = promise.GetFuture( - base::SequencedTaskRunnerHandle::Get()); - base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::BindOnce([](Promise<void> promise) { - promise.SetValue(); - }, std::move(promise))); - future.Get(); - } -} - -} // namespace brillo |