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);
+}

Reply via email to