Repository: mesos Updated Branches: refs/heads/1.3.x 07468938f -> 596191ac8
Added an undiscardable() helper that blocks discards from propagating. This can be useful in circumstances where you don't want some asynchronous operation to be canceled. Review: https://reviews.apache.org/r/61987 Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/4846a5bc Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/4846a5bc Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/4846a5bc Branch: refs/heads/1.3.x Commit: 4846a5bc756ee662c9ab7daa72ef426b08abe3db Parents: 0746893 Author: Benjamin Hindman <benjamin.hind...@gmail.com> Authored: Tue Aug 29 23:23:36 2017 -0700 Committer: Jie Yu <yujie....@gmail.com> Committed: Thu Aug 31 19:55:01 2017 -0700 ---------------------------------------------------------------------- 3rdparty/libprocess/include/process/future.hpp | 91 +++++++++++++++++++++ 3rdparty/libprocess/src/tests/future_tests.cpp | 41 ++++++++++ 2 files changed, 132 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/4846a5bc/3rdparty/libprocess/include/process/future.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/include/process/future.hpp b/3rdparty/libprocess/include/process/future.hpp index cce9505..85f3b7c 100644 --- a/3rdparty/libprocess/include/process/future.hpp +++ b/3rdparty/libprocess/include/process/future.hpp @@ -1621,6 +1621,97 @@ void discardPromises(std::set<Promise<T>*>* promises, const Future<T>& future) } } + +// Returns a future that will not propagate a discard through to the +// future passed in as an argument. This can be very valuable if you +// want to block some future from getting discarded. +// +// Example: +// +// Promise<int> promise; +// Future<int> future = undiscardable(promise.future()); +// future.discard(); +// assert(!promise.future().hasDiscard()); +// +// Or another example, when chaining futures: +// +// Future<int> future = undiscardable( +// foo() +// .then([]() { ...; }) +// .then([]() { ...; })); +// +// This will guarantee that a discard _will not_ propagate to `foo()` +// or any of the futures returned from the invocations of `.then()`. +template <typename T> +Future<T> undiscardable(const Future<T>& future) +{ + std::shared_ptr<Promise<T>> promise(new Promise<T>()); + future.onAny([promise](const Future<T>& future) { + promise->associate(future); + }); + return promise->future(); +} + + +// Decorator that for some callable `f` invokes +// `undiscardable(f(args))` for some `args`. This is used by the +// overload of `undiscardable()` that takes callables instead of a +// specialization of `Future`. +// +// TODO(benh): Factor out a generic decorator pattern to be used in +// other circumstances, e.g., to replace `_Deferred`. +template <typename F> +struct UndiscardableDecorator +{ + template < + typename G, + typename std::enable_if< + std::is_constructible<F, G>::value, int>::type = 0> + UndiscardableDecorator(G&& g) : f(std::forward<G>(g)) {} + + template <typename... Args> + auto operator()(Args&&... args) + -> decltype(std::declval<F&>()(std::forward<Args>(args)...)) + { + using Result = + typename std::decay<decltype(f(std::forward<Args>(args)...))>::type; + + static_assert( + is_specialization_of<Future, Result>::value, + "Expecting Future<T> to be returned from undiscarded(...)"); + + return undiscardable(f(std::forward<Args>(args)...)); + } + + F f; +}; + + +// An overload of `undiscardable()` above that takes and returns a +// callable. The returned callable has decorated the provided callable +// `f` such that when the returned callable is invoked it will in turn +// invoke `undiscardable(f(args))` for some `args`. See +// `UndiscardableDecorator` above for more details. +// +// Example: +// +// Future<int> future = foo() +// .then(undiscardable([]() { ...; })); +// +// This guarantees that even if `future` is discarded the discard will +// not propagate into the lambda passed into `.then()`. +template < + typename F, + typename std::enable_if< + !is_specialization_of< + Future, + typename std::decay<F>::type>::value, int>::type = 0> +UndiscardableDecorator<typename std::decay<F>::type> undiscardable(F&& f) +{ + return UndiscardableDecorator< + typename std::decay<F>::type>(std::forward<F>(f)); +} + } // namespace process { #endif // __PROCESS_FUTURE_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/4846a5bc/3rdparty/libprocess/src/tests/future_tests.cpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/src/tests/future_tests.cpp b/3rdparty/libprocess/src/tests/future_tests.cpp index f21361e..61e0e45 100644 --- a/3rdparty/libprocess/src/tests/future_tests.cpp +++ b/3rdparty/libprocess/src/tests/future_tests.cpp @@ -26,6 +26,7 @@ using process::Clock; using process::Failure; using process::Future; using process::Promise; +using process::undiscardable; using std::string; @@ -548,3 +549,43 @@ TEST(FutureTest, ArrowOperator) Future<string> s = string("hello"); EXPECT_EQ(5u, s->size()); } + + +TEST(FutureTest, UndiscardableFuture) +{ + Promise<int> promise; + + Future<int> f = undiscardable(promise.future()); + + f.discard(); + + EXPECT_TRUE(f.hasDiscard()); + EXPECT_FALSE(promise.future().hasDiscard()); + + promise.set(42); + + AWAIT_ASSERT_EQ(42, f); +} + + +TEST(FutureTest, UndiscardableLambda) +{ + Promise<int> promise; + + Future<int> f = Future<int>(2) + .then(undiscardable([&](int multiplier) { + return promise.future() + .then([=](int i) { + return i * multiplier; + }); + })); + + f.discard(); + + EXPECT_TRUE(f.hasDiscard()); + EXPECT_FALSE(promise.future().hasDiscard()); + + promise.set(42); + + AWAIT_ASSERT_EQ(84, f); +}