aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWoody Chow <woodychow@google.com>2020-07-07 12:47:05 +0900
committerCommit Bot <commit-bot@chromium.org>2020-07-07 16:37:43 +0000
commitd58198336b6edbb489c0a3d39ec2ac793a96afeb (patch)
tree9da9751f965ac8cdb8411a0766dad4f0676e1083
parentfe014639c72a2214d31e235654d2926e05418beb (diff)
downloadplatform_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.gn1
-rw-r--r--brillo/apply_impl.h34
-rw-r--r--brillo/future.h644
-rw-r--r--brillo/future_test.cc478
4 files changed, 0 insertions, 1157 deletions
diff --git a/BUILD.gn b/BUILD.gn
index de98be4..2fb8367 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -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