codemod: folly/wangle/ -> folly/wangle/futures
authorJames Sedgwick <jsedgwick@fb.com>
Tue, 16 Dec 2014 18:26:24 +0000 (10:26 -0800)
committerJoelMarcey <joelm@fb.com>
Thu, 18 Dec 2014 20:29:40 +0000 (12:29 -0800)
Summary: Last thing before moving experimental/wangle here. Once everything is in the same directory I'm probably going to consolidate experimental/wangle/concurrent with the executors in this directory into wangle/executors/. And probably rename some of these targets. For now, a dumb move is enough.

Test Plan: waiting for contbuild

Reviewed By: davejwatson@fb.com

Subscribers: trunkagent, fbcode-common-diffs@, chaoyc, search-fbcode-diffs@, lars, ruibalp, hero-diffs@, zeus-diffs@, vikas, danielg, mcduff, cold-storage-diffs@, unicorn-diffs@, ldbrandy, targeting-diff-backend@, netego-diffs@, fugalh, adamsyta, atlas2-eng@, alandau, bmatheny, adityab, everstore-dev@, zhuohuang, sweeney, mwa, jgehring, smarlow, akr, bnitka, jcoens, luk, zhguo, jying, apodsiadlo, alikhtarov, fuegen, dzhulgakov, mshneer, folly-diffs@, wch, tingy, hannesr

FB internal diff: D1740327

Tasks: 5802833

Signature: t1:1740327:1418752541:82d7486293b0a12938730ae66d480c120aded4dc

69 files changed:
folly/Makefile.am
folly/experimental/wangle/channel/ChannelHandler.h
folly/experimental/wangle/channel/ChannelHandlerContext.h
folly/experimental/wangle/channel/ChannelPipeline.h
folly/experimental/wangle/concurrent/FutureExecutor.h
folly/wangle/Deprecated.h [deleted file]
folly/wangle/Future-inl.h [deleted file]
folly/wangle/Future.h [deleted file]
folly/wangle/InlineExecutor.cpp [deleted file]
folly/wangle/InlineExecutor.h [deleted file]
folly/wangle/ManualExecutor.cpp [deleted file]
folly/wangle/ManualExecutor.h [deleted file]
folly/wangle/OpaqueCallbackShunt.h [deleted file]
folly/wangle/Promise-inl.h [deleted file]
folly/wangle/Promise.h [deleted file]
folly/wangle/QueuedImmediateExecutor.cpp [deleted file]
folly/wangle/QueuedImmediateExecutor.h [deleted file]
folly/wangle/README.md [deleted file]
folly/wangle/ScheduledExecutor.h [deleted file]
folly/wangle/Try-inl.h [deleted file]
folly/wangle/Try.h [deleted file]
folly/wangle/WangleException.h [deleted file]
folly/wangle/detail/Core.h [deleted file]
folly/wangle/detail/Dummy.cpp [deleted file]
folly/wangle/detail/FSM.h [deleted file]
folly/wangle/futures/Deprecated.h [new file with mode: 0644]
folly/wangle/futures/Future-inl.h [new file with mode: 0644]
folly/wangle/futures/Future.h [new file with mode: 0644]
folly/wangle/futures/InlineExecutor.cpp [new file with mode: 0644]
folly/wangle/futures/InlineExecutor.h [new file with mode: 0644]
folly/wangle/futures/ManualExecutor.cpp [new file with mode: 0644]
folly/wangle/futures/ManualExecutor.h [new file with mode: 0644]
folly/wangle/futures/OpaqueCallbackShunt.h [new file with mode: 0644]
folly/wangle/futures/Promise-inl.h [new file with mode: 0644]
folly/wangle/futures/Promise.h [new file with mode: 0644]
folly/wangle/futures/QueuedImmediateExecutor.cpp [new file with mode: 0644]
folly/wangle/futures/QueuedImmediateExecutor.h [new file with mode: 0644]
folly/wangle/futures/README.md [new file with mode: 0644]
folly/wangle/futures/ScheduledExecutor.h [new file with mode: 0644]
folly/wangle/futures/Try-inl.h [new file with mode: 0644]
folly/wangle/futures/Try.h [new file with mode: 0644]
folly/wangle/futures/WangleException.h [new file with mode: 0644]
folly/wangle/futures/detail/Core.h [new file with mode: 0644]
folly/wangle/futures/detail/Dummy.cpp [new file with mode: 0644]
folly/wangle/futures/detail/FSM.h [new file with mode: 0644]
folly/wangle/futures/test/Benchmark.cpp [new file with mode: 0644]
folly/wangle/futures/test/ClientCompile.cpp [new file with mode: 0644]
folly/wangle/futures/test/ExecutorTest.cpp [new file with mode: 0644]
folly/wangle/futures/test/FSM.cpp [new file with mode: 0644]
folly/wangle/futures/test/FutureTest.cpp [new file with mode: 0644]
folly/wangle/futures/test/Interrupts.cpp [new file with mode: 0644]
folly/wangle/futures/test/Thens.cpp [new file with mode: 0644]
folly/wangle/futures/test/Thens.h [new file with mode: 0644]
folly/wangle/futures/test/Try.cpp [new file with mode: 0644]
folly/wangle/futures/test/ViaTest.cpp [new file with mode: 0644]
folly/wangle/futures/test/main.cpp [new file with mode: 0644]
folly/wangle/futures/test/thens.rb [new file with mode: 0755]
folly/wangle/test/Benchmark.cpp [deleted file]
folly/wangle/test/ClientCompile.cpp [deleted file]
folly/wangle/test/ExecutorTest.cpp [deleted file]
folly/wangle/test/FSM.cpp [deleted file]
folly/wangle/test/FutureTest.cpp [deleted file]
folly/wangle/test/Interrupts.cpp [deleted file]
folly/wangle/test/Thens.cpp [deleted file]
folly/wangle/test/Thens.h [deleted file]
folly/wangle/test/Try.cpp [deleted file]
folly/wangle/test/ViaTest.cpp [deleted file]
folly/wangle/test/main.cpp [deleted file]
folly/wangle/test/thens.rb [deleted file]

index 2da845952bd288112432588cec82dda033941642..74bbb43fa6fc9060752740176bf6ba4e32b3e4a1 100644 (file)
@@ -237,21 +237,21 @@ nobase_follyinclude_HEADERS = \
        Uri-inl.h \
        Varint.h \
        VersionCheck.h \
-       wangle/Deprecated.h \
-       wangle/Future-inl.h \
-       wangle/Future.h \
-       wangle/InlineExecutor.h \
-       wangle/ManualExecutor.h \
-       wangle/OpaqueCallbackShunt.h \
-       wangle/Promise-inl.h \
-       wangle/Promise.h \
-       wangle/QueuedImmediateExecutor.h \
-       wangle/ScheduledExecutor.h \
-       wangle/Try-inl.h \
-       wangle/Try.h \
-       wangle/WangleException.h \
-       wangle/detail/Core.h \
-       wangle/detail/FSM.h
+       wangle/futures/Deprecated.h \
+       wangle/futures/Future-inl.h \
+       wangle/futures/Future.h \
+       wangle/futures/InlineExecutor.h \
+       wangle/futures/ManualExecutor.h \
+       wangle/futures/OpaqueCallbackShunt.h \
+       wangle/futures/Promise-inl.h \
+       wangle/futures/Promise.h \
+       wangle/futures/QueuedImmediateExecutor.h \
+       wangle/futures/ScheduledExecutor.h \
+       wangle/futures/Try-inl.h \
+       wangle/futures/Try.h \
+       wangle/futures/WangleException.h \
+       wangle/futures/detail/Core.h \
+       wangle/futures/detail/FSM.h
 
 FormatTables.cpp: build/generate_format_tables.py
        build/generate_format_tables.py
@@ -323,8 +323,8 @@ libfolly_la_SOURCES = \
        TimeoutQueue.cpp \
        Uri.cpp \
        Version.cpp \
-       wangle/InlineExecutor.cpp \
-       wangle/ManualExecutor.cpp \
+       wangle/futures/InlineExecutor.cpp \
+       wangle/futures/ManualExecutor.cpp \
        experimental/io/FsUtil.cpp \
        experimental/Singleton.cpp \
        experimental/TestUtil.cpp \
index 27a324470631af6b084a3a24b668c27296599187..e7fd3135b26b3c92ee408005ba0675e8efbff594 100644 (file)
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include <folly/wangle/Future.h>
+#include <folly/wangle/futures/Future.h>
 #include <folly/experimental/wangle/channel/ChannelPipeline.h>
 #include <folly/io/IOBuf.h>
 #include <folly/io/IOBufQueue.h>
index baf413ff969413ef28269e26e531dc139ecfe742..59ea3ae446d1afe2b8fbc8691a40cd1d983837d8 100644 (file)
@@ -17,7 +17,7 @@
 #pragma once
 
 #include <folly/io/async/AsyncTransport.h>
-#include <folly/wangle/Future.h>
+#include <folly/wangle/futures/Future.h>
 #include <folly/ExceptionWrapper.h>
 
 namespace folly { namespace wangle {
index ea7959bc880fd4f10508ae3901f8d14c5da72448..386caec3528a76d87202dc502712dc17ec5b5b82 100644 (file)
@@ -17,7 +17,7 @@
 #pragma once
 
 #include <folly/experimental/wangle/channel/ChannelHandlerContext.h>
-#include <folly/wangle/Future.h>
+#include <folly/wangle/futures/Future.h>
 #include <folly/io/async/AsyncTransport.h>
 #include <folly/io/async/DelayedDestruction.h>
 #include <folly/ExceptionWrapper.h>
index ecf392b7de6e908fa3ed83e0266c63a4f8f1cee2..8aeedff83844fe791deff7fb6b0eee71cf6245be 100644 (file)
@@ -15,7 +15,7 @@
  */
 
 #pragma once
-#include <folly/wangle/Future.h>
+#include <folly/wangle/futures/Future.h>
 
 namespace folly { namespace wangle {
 
diff --git a/folly/wangle/Deprecated.h b/folly/wangle/Deprecated.h
deleted file mode 100644 (file)
index 75937b1..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#define DEPRECATED __attribute__((__deprecated__))
diff --git a/folly/wangle/Future-inl.h b/folly/wangle/Future-inl.h
deleted file mode 100644 (file)
index 2df6fef..0000000
+++ /dev/null
@@ -1,631 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <chrono>
-#include <thread>
-
-#include <folly/wangle/detail/Core.h>
-#include <folly/Baton.h>
-
-namespace folly { namespace wangle {
-
-template <typename T>
-struct isFuture {
-  static const bool value = false;
-};
-
-template <typename T>
-struct isFuture<Future<T> > {
-  static const bool value = true;
-};
-
-template <class T>
-Future<T>::Future(Future<T>&& other) noexcept : core_(nullptr) {
-  *this = std::move(other);
-}
-
-template <class T>
-Future<T>& Future<T>::operator=(Future<T>&& other) {
-  std::swap(core_, other.core_);
-  return *this;
-}
-
-template <class T>
-Future<T>::~Future() {
-  detach();
-}
-
-template <class T>
-void Future<T>::detach() {
-  if (core_) {
-    core_->detachFuture();
-    core_ = nullptr;
-  }
-}
-
-template <class T>
-void Future<T>::throwIfInvalid() const {
-  if (!core_)
-    throw NoState();
-}
-
-template <class T>
-template <class F>
-void Future<T>::setCallback_(F&& func) {
-  throwIfInvalid();
-  core_->setCallback(std::move(func));
-}
-
-// Variant: f.then([](Try<T>&& t){ return t.value(); });
-template <class T>
-template <class F>
-typename std::enable_if<
-  !isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
-  Future<typename std::result_of<F(Try<T>&&)>::type> >::type
-Future<T>::then(F&& func) {
-  typedef typename std::result_of<F(Try<T>&&)>::type B;
-
-  throwIfInvalid();
-
-  // wrap these so we can move them into the lambda
-  folly::MoveWrapper<Promise<B>> p;
-  folly::MoveWrapper<F> funcm(std::forward<F>(func));
-
-  // grab the Future now before we lose our handle on the Promise
-  auto f = p->getFuture();
-
-  /* This is a bit tricky.
-
-     We can't just close over *this in case this Future gets moved. So we
-     make a new dummy Future. We could figure out something more
-     sophisticated that avoids making a new Future object when it can, as an
-     optimization. But this is correct.
-
-     core_ can't be moved, it is explicitly disallowed (as is copying). But
-     if there's ever a reason to allow it, this is one place that makes that
-     assumption and would need to be fixed. We use a standard shared pointer
-     for core_ (by copying it in), which means in essence obj holds a shared
-     pointer to itself.  But this shouldn't leak because Promise will not
-     outlive the continuation, because Promise will setException() with a
-     broken Promise if it is destructed before completed. We could use a
-     weak pointer but it would have to be converted to a shared pointer when
-     func is executed (because the Future returned by func may possibly
-     persist beyond the callback, if it gets moved), and so it is an
-     optimization to just make it shared from the get-go.
-
-     We have to move in the Promise and func using the MoveWrapper
-     hack. (func could be copied but it's a big drag on perf).
-
-     Two subtle but important points about this design. detail::Core has no
-     back pointers to Future or Promise, so if Future or Promise get moved
-     (and they will be moved in performant code) we don't have to do
-     anything fancy. And because we store the continuation in the
-     detail::Core, not in the Future, we can execute the continuation even
-     after the Future has gone out of scope. This is an intentional design
-     decision. It is likely we will want to be able to cancel a continuation
-     in some circumstances, but I think it should be explicit not implicit
-     in the destruction of the Future used to create it.
-     */
-  setCallback_(
-    [p, funcm](Try<T>&& t) mutable {
-      p->fulfil([&]() {
-          return (*funcm)(std::move(t));
-        });
-    });
-
-  return std::move(f);
-}
-
-// Variant: f.then([](T&& t){ return t; });
-template <class T>
-template <class F>
-typename std::enable_if<
-  !std::is_same<T, void>::value &&
-  !isFuture<typename std::result_of<
-    F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
-  Future<typename std::result_of<
-    F(typename detail::AliasIfVoid<T>::type&&)>::type> >::type
-Future<T>::then(F&& func) {
-  typedef typename std::result_of<F(T&&)>::type B;
-
-  throwIfInvalid();
-
-  folly::MoveWrapper<Promise<B>> p;
-  folly::MoveWrapper<F> funcm(std::forward<F>(func));
-  auto f = p->getFuture();
-
-  setCallback_(
-    [p, funcm](Try<T>&& t) mutable {
-      if (t.hasException()) {
-        p->setException(t.getException());
-      } else {
-        p->fulfil([&]() {
-          return (*funcm)(std::move(t.value()));
-        });
-      }
-    });
-
-  return std::move(f);
-}
-
-// Variant: f.then([](){ return; });
-template <class T>
-template <class F>
-typename std::enable_if<
-  std::is_same<T, void>::value &&
-  !isFuture<typename std::result_of<F()>::type>::value,
-  Future<typename std::result_of<F()>::type> >::type
-Future<T>::then(F&& func) {
-  typedef typename std::result_of<F()>::type B;
-
-  throwIfInvalid();
-
-  folly::MoveWrapper<Promise<B>> p;
-  folly::MoveWrapper<F> funcm(std::forward<F>(func));
-  auto f = p->getFuture();
-
-  setCallback_(
-    [p, funcm](Try<T>&& t) mutable {
-      if (t.hasException()) {
-        p->setException(t.getException());
-      } else {
-        p->fulfil([&]() {
-          return (*funcm)();
-        });
-      }
-    });
-
-  return std::move(f);
-}
-
-// Variant: f.then([](Try<T>&& t){ return makeFuture<T>(t.value()); });
-template <class T>
-template <class F>
-typename std::enable_if<
-  isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
-  Future<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
-Future<T>::then(F&& func) {
-  typedef typename std::result_of<F(Try<T>&&)>::type::value_type B;
-
-  throwIfInvalid();
-
-  // wrap these so we can move them into the lambda
-  folly::MoveWrapper<Promise<B>> p;
-  folly::MoveWrapper<F> funcm(std::forward<F>(func));
-
-  // grab the Future now before we lose our handle on the Promise
-  auto f = p->getFuture();
-
-  setCallback_(
-    [p, funcm](Try<T>&& t) mutable {
-      try {
-        auto f2 = (*funcm)(std::move(t));
-        // that didn't throw, now we can steal p
-        f2.setCallback_([p](Try<B>&& b) mutable {
-            p->fulfilTry(std::move(b));
-          });
-      } catch (...) {
-        p->setException(std::current_exception());
-      }
-    });
-
-  return std::move(f);
-}
-
-// Variant: f.then([](T&& t){ return makeFuture<T>(t); });
-template <class T>
-template <class F>
-typename std::enable_if<
-  !std::is_same<T, void>::value &&
-  isFuture<typename std::result_of<
-    F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
-  Future<typename std::result_of<
-    F(typename detail::AliasIfVoid<T>::type&&)>::type::value_type> >::type
-Future<T>::then(F&& func) {
-  typedef typename std::result_of<F(T&&)>::type::value_type B;
-
-  throwIfInvalid();
-
-  folly::MoveWrapper<Promise<B>> p;
-  folly::MoveWrapper<F> funcm(std::forward<F>(func));
-  auto f = p->getFuture();
-
-  setCallback_(
-    [p, funcm](Try<T>&& t) mutable {
-      if (t.hasException()) {
-        p->setException(t.getException());
-      } else {
-        try {
-          auto f2 = (*funcm)(std::move(t.value()));
-          f2.setCallback_([p](Try<B>&& b) mutable {
-              p->fulfilTry(std::move(b));
-            });
-        } catch (...) {
-          p->setException(std::current_exception());
-        }
-      }
-    });
-
-  return std::move(f);
-}
-
-// Variant: f.then([](){ return makeFuture(); });
-template <class T>
-template <class F>
-typename std::enable_if<
-  std::is_same<T, void>::value &&
-  isFuture<typename std::result_of<F()>::type>::value,
-  Future<typename std::result_of<F()>::type::value_type> >::type
-Future<T>::then(F&& func) {
-  typedef typename std::result_of<F()>::type::value_type B;
-
-  throwIfInvalid();
-
-  folly::MoveWrapper<Promise<B>> p;
-  folly::MoveWrapper<F> funcm(std::forward<F>(func));
-
-  auto f = p->getFuture();
-
-  setCallback_(
-    [p, funcm](Try<T>&& t) mutable {
-      if (t.hasException()) {
-        p->setException(t.getException());
-      } else {
-        try {
-          auto f2 = (*funcm)();
-          f2.setCallback_([p](Try<B>&& b) mutable {
-              p->fulfilTry(std::move(b));
-            });
-        } catch (...) {
-          p->setException(std::current_exception());
-        }
-      }
-    });
-
-  return std::move(f);
-}
-
-template <class T>
-Future<void> Future<T>::then() {
-  return then([] (Try<T>&& t) {});
-}
-
-template <class T>
-typename std::add_lvalue_reference<T>::type Future<T>::value() {
-  throwIfInvalid();
-
-  return core_->getTry().value();
-}
-
-template <class T>
-typename std::add_lvalue_reference<const T>::type Future<T>::value() const {
-  throwIfInvalid();
-
-  return core_->getTry().value();
-}
-
-template <class T>
-Try<T>& Future<T>::getTry() {
-  throwIfInvalid();
-
-  return core_->getTry();
-}
-
-template <class T>
-template <typename Executor>
-inline Future<T> Future<T>::via(Executor* executor) && {
-  throwIfInvalid();
-
-  this->deactivate();
-  core_->setExecutor(executor);
-
-  return std::move(*this);
-}
-
-template <class T>
-template <typename Executor>
-inline Future<T> Future<T>::via(Executor* executor) & {
-  throwIfInvalid();
-
-  MoveWrapper<Promise<T>> p;
-  auto f = p->getFuture();
-  then([p](Try<T>&& t) mutable { p->fulfilTry(std::move(t)); });
-  return std::move(f).via(executor);
-}
-
-template <class T>
-bool Future<T>::isReady() const {
-  throwIfInvalid();
-  return core_->ready();
-}
-
-template <class T>
-void Future<T>::raise(std::exception_ptr exception) {
-  core_->raise(exception);
-}
-
-// makeFuture
-
-template <class T>
-Future<typename std::decay<T>::type> makeFuture(T&& t) {
-  Promise<typename std::decay<T>::type> p;
-  auto f = p.getFuture();
-  p.setValue(std::forward<T>(t));
-  return std::move(f);
-}
-
-inline // for multiple translation units
-Future<void> makeFuture() {
-  Promise<void> p;
-  auto f = p.getFuture();
-  p.setValue();
-  return std::move(f);
-}
-
-template <class F>
-auto makeFutureTry(
-    F&& func,
-    typename std::enable_if<!std::is_reference<F>::value, bool>::type sdf)
-    -> Future<decltype(func())> {
-  Promise<decltype(func())> p;
-  auto f = p.getFuture();
-  p.fulfil(
-    [&func]() {
-      return (func)();
-    });
-  return std::move(f);
-}
-
-template <class F>
-auto makeFutureTry(F const& func) -> Future<decltype(func())> {
-  F copy = func;
-  return makeFutureTry(std::move(copy));
-}
-
-template <class T>
-Future<T> makeFuture(std::exception_ptr const& e) {
-  Promise<T> p;
-  auto f = p.getFuture();
-  p.setException(e);
-  return std::move(f);
-}
-
-template <class T, class E>
-typename std::enable_if<std::is_base_of<std::exception, E>::value,
-                        Future<T>>::type
-makeFuture(E const& e) {
-  Promise<T> p;
-  auto f = p.getFuture();
-  p.fulfil([&]() -> T { throw e; });
-  return std::move(f);
-}
-
-template <class T>
-Future<T> makeFuture(Try<T>&& t) {
-  try {
-    return makeFuture<T>(std::move(t.value()));
-  } catch (...) {
-    return makeFuture<T>(std::current_exception());
-  }
-}
-
-template <>
-inline Future<void> makeFuture(Try<void>&& t) {
-  try {
-    t.throwIfFailed();
-    return makeFuture();
-  } catch (...) {
-    return makeFuture<void>(std::current_exception());
-  }
-}
-
-// via
-template <typename Executor>
-Future<void> via(Executor* executor) {
-  return makeFuture().via(executor);
-}
-
-// when (variadic)
-
-template <typename... Fs>
-typename detail::VariadicContext<
-  typename std::decay<Fs>::type::value_type...>::type
-whenAll(Fs&&... fs)
-{
-  auto ctx =
-    new detail::VariadicContext<typename std::decay<Fs>::type::value_type...>();
-  ctx->total = sizeof...(fs);
-  auto f_saved = ctx->p.getFuture();
-  detail::whenAllVariadicHelper(ctx,
-    std::forward<typename std::decay<Fs>::type>(fs)...);
-  return std::move(f_saved);
-}
-
-// when (iterator)
-
-template <class InputIterator>
-Future<
-  std::vector<
-  Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
-whenAll(InputIterator first, InputIterator last)
-{
-  typedef
-    typename std::iterator_traits<InputIterator>::value_type::value_type T;
-
-  auto n = std::distance(first, last);
-  if (n == 0) {
-    return makeFuture(std::vector<Try<T>>());
-  }
-
-  auto ctx = new detail::WhenAllContext<T>();
-
-  ctx->results.resize(n);
-
-  auto f_saved = ctx->p.getFuture();
-
-  for (size_t i = 0; first != last; ++first, ++i) {
-     assert(i < n);
-     auto& f = *first;
-     f.setCallback_([ctx, i, n](Try<T>&& t) {
-         ctx->results[i] = std::move(t);
-         if (++ctx->count == n) {
-           ctx->p.setValue(std::move(ctx->results));
-           delete ctx;
-         }
-       });
-  }
-
-  return std::move(f_saved);
-}
-
-template <class InputIterator>
-Future<
-  std::pair<size_t,
-            Try<
-              typename
-              std::iterator_traits<InputIterator>::value_type::value_type> > >
-whenAny(InputIterator first, InputIterator last) {
-  typedef
-    typename std::iterator_traits<InputIterator>::value_type::value_type T;
-
-  auto ctx = new detail::WhenAnyContext<T>(std::distance(first, last));
-  auto f_saved = ctx->p.getFuture();
-
-  for (size_t i = 0; first != last; first++, i++) {
-    auto& f = *first;
-    f.setCallback_([i, ctx](Try<T>&& t) {
-      if (!ctx->done.exchange(true)) {
-        ctx->p.setValue(std::make_pair(i, std::move(t)));
-      }
-      ctx->decref();
-    });
-  }
-
-  return std::move(f_saved);
-}
-
-template <class InputIterator>
-Future<std::vector<std::pair<size_t, Try<typename
-  std::iterator_traits<InputIterator>::value_type::value_type>>>>
-whenN(InputIterator first, InputIterator last, size_t n) {
-  typedef typename
-    std::iterator_traits<InputIterator>::value_type::value_type T;
-  typedef std::vector<std::pair<size_t, Try<T>>> V;
-
-  struct ctx_t {
-    V v;
-    size_t completed;
-    Promise<V> p;
-  };
-  auto ctx = std::make_shared<ctx_t>();
-  ctx->completed = 0;
-
-  // for each completed Future, increase count and add to vector, until we
-  // have n completed futures at which point we fulfil our Promise with the
-  // vector
-  auto it = first;
-  size_t i = 0;
-  while (it != last) {
-    it->then([ctx, n, i](Try<T>&& t) {
-      auto& v = ctx->v;
-      auto c = ++ctx->completed;
-      if (c <= n) {
-        assert(ctx->v.size() < n);
-        v.push_back(std::make_pair(i, std::move(t)));
-        if (c == n) {
-          ctx->p.fulfilTry(Try<V>(std::move(v)));
-        }
-      }
-    });
-
-    it++;
-    i++;
-  }
-
-  if (i < n) {
-    ctx->p.setException(std::runtime_error("Not enough futures"));
-  }
-
-  return ctx->p.getFuture();
-}
-
-template <typename T>
-Future<T>
-waitWithSemaphore(Future<T>&& f) {
-  Baton<> baton;
-  auto done = f.then([&](Try<T> &&t) {
-    baton.post();
-    return std::move(t.value());
-  });
-  baton.wait();
-  while (!done.isReady()) {
-    // There's a race here between the return here and the actual finishing of
-    // the future. f is completed, but the setup may not have finished on done
-    // after the baton has posted.
-    std::this_thread::yield();
-  }
-  return done;
-}
-
-template<>
-inline Future<void> waitWithSemaphore<void>(Future<void>&& f) {
-  Baton<> baton;
-  auto done = f.then([&](Try<void> &&t) {
-    baton.post();
-    t.value();
-  });
-  baton.wait();
-  while (!done.isReady()) {
-    // There's a race here between the return here and the actual finishing of
-    // the future. f is completed, but the setup may not have finished on done
-    // after the baton has posted.
-    std::this_thread::yield();
-  }
-  return done;
-}
-
-template <typename T, class Duration>
-Future<T>
-waitWithSemaphore(Future<T>&& f, Duration timeout) {
-  auto baton = std::make_shared<Baton<>>();
-  auto done = f.then([baton](Try<T> &&t) {
-    baton->post();
-    return std::move(t.value());
-  });
-  baton->timed_wait(std::chrono::system_clock::now() + timeout);
-  return done;
-}
-
-template <class Duration>
-Future<void>
-waitWithSemaphore(Future<void>&& f, Duration timeout) {
-  auto baton = std::make_shared<Baton<>>();
-  auto done = f.then([baton](Try<void> &&t) {
-    baton->post();
-    t.value();
-  });
-  baton->timed_wait(std::chrono::system_clock::now() + timeout);
-  return done;
-}
-
-}}
-
-// I haven't included a Future<T&> specialization because I don't forsee us
-// using it, however it is not difficult to add when needed. Refer to
-// Future<void> for guidance. std::future and boost::future code would also be
-// instructive.
diff --git a/folly/wangle/Future.h b/folly/wangle/Future.h
deleted file mode 100644 (file)
index f124d7b..0000000
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <algorithm>
-#include <exception>
-#include <functional>
-#include <memory>
-#include <type_traits>
-#include <vector>
-
-#include <folly/MoveWrapper.h>
-#include <folly/wangle/Promise.h>
-#include <folly/wangle/Try.h>
-
-namespace folly { namespace wangle {
-
-namespace detail {
-  template <class> struct Core;
-  template <class...> struct VariadicContext;
-
-  template <class T>
-  struct AliasIfVoid {
-    typedef typename std::conditional<
-      std::is_same<T, void>::value,
-      int,
-      T>::type type;
-  };
-}
-
-template <class> struct Promise;
-
-template <typename T> struct isFuture;
-
-template <class T>
-class Future {
- public:
-  typedef T value_type;
-
-  // not copyable
-  Future(Future const&) = delete;
-  Future& operator=(Future const&) = delete;
-
-  // movable
-  Future(Future&&) noexcept;
-  Future& operator=(Future&&);
-
-  ~Future();
-
-  /** Return the reference to result. Should not be called if !isReady().
-    Will rethrow the exception if an exception has been
-    captured.
-
-    This function is not thread safe - the returned Future can only
-    be executed from the thread that the executor runs it in.
-    See below for a thread safe version
-    */
-  typename std::add_lvalue_reference<T>::type
-  value();
-  typename std::add_lvalue_reference<const T>::type
-  value() const;
-
-  /// Returns an inactive Future which will call back on the other side of
-  /// executor (when it is activated).
-  ///
-  /// NB remember that Futures activate when they destruct. This is good,
-  /// it means that this will work:
-  ///
-  ///   f.via(e).then(a).then(b);
-  ///
-  /// a and b will execute in the same context (the far side of e), because
-  /// the Future (temporary variable) created by via(e) does not call back
-  /// until it destructs, which is after then(a) and then(b) have been wired
-  /// up.
-  ///
-  /// But this is still racy:
-  ///
-  ///   f = f.via(e).then(a);
-  ///   f.then(b);
-  // The ref-qualifier allows for `this` to be moved out so we
-  // don't get access-after-free situations in chaining.
-  // https://akrzemi1.wordpress.com/2014/06/02/ref-qualifiers/
-  template <typename Executor>
-  Future<T> via(Executor* executor) &&;
-
-  /// This variant creates a new future, where the ref-qualifier && version
-  /// moves `this` out. This one is less efficient but avoids confusing users
-  /// when "return f.via(x);" fails.
-  template <typename Executor>
-  Future<T> via(Executor* executor) &;
-
-  /** True when the result (or exception) is ready. */
-  bool isReady() const;
-
-  /** A reference to the Try of the value */
-  Try<T>& getTry();
-
-  /** When this Future has completed, execute func which is a function that
-    takes a Try<T>&&. A Future for the return type of func is
-    returned. e.g.
-
-    Future<string> f2 = f1.then([](Try<T>&&) { return string("foo"); });
-
-    The Future given to the functor is ready, and the functor may call
-    value(), which may rethrow if this has captured an exception. If func
-    throws, the exception will be captured in the Future that is returned.
-    */
-  /* TODO n3428 and other async frameworks have something like then(scheduler,
-     Future), we might want to support a similar API which could be
-     implemented a little more efficiently than
-     f.via(executor).then(callback) */
-  template <class F>
-  typename std::enable_if<
-    !isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
-    Future<typename std::result_of<F(Try<T>&&)>::type> >::type
-  then(F&& func);
-
-  /// Variant where func takes a T directly, bypassing a try. Any exceptions
-  /// will be implicitly passed on to the resultant Future.
-  ///
-  ///   Future<int> f = makeFuture<int>(42).then([](int i) { return i+1; });
-  template <class F>
-  typename std::enable_if<
-    !std::is_same<T, void>::value &&
-    !isFuture<typename std::result_of<
-      F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
-    Future<typename std::result_of<
-      F(typename detail::AliasIfVoid<T>::type&&)>::type> >::type
-  then(F&& func);
-
-  /// Like the above variant, but for void futures. That is, func takes no
-  /// argument.
-  ///
-  ///   Future<int> f = makeFuture().then([] { return 42; });
-  template <class F>
-  typename std::enable_if<
-    std::is_same<T, void>::value &&
-    !isFuture<typename std::result_of<F()>::type>::value,
-    Future<typename std::result_of<F()>::type> >::type
-  then(F&& func);
-
-  /// Variant where func returns a Future<T> instead of a T. e.g.
-  ///
-  ///   Future<string> f2 = f1.then(
-  ///     [](Try<T>&&) { return makeFuture<string>("foo"); });
-  template <class F>
-  typename std::enable_if<
-    isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
-    Future<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
-  then(F&& func);
-
-  /// Variant where func returns a Future<T2> and takes a T directly, bypassing
-  /// a Try. Any exceptions will be implicitly passed on to the resultant
-  /// Future. For example,
-  ///
-  ///   Future<int> f = makeFuture<int>(42).then(
-  ///     [](int i) { return makeFuture<int>(i+1); });
-  template <class F>
-  typename std::enable_if<
-    !std::is_same<T, void>::value &&
-    isFuture<typename std::result_of<
-      F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
-    Future<typename std::result_of<
-      F(typename detail::AliasIfVoid<T>::type&&)>::type::value_type> >::type
-  then(F&& func);
-
-  /// Like the above variant, but for void futures. That is, func takes no
-  /// argument and returns a future.
-  ///
-  ///   Future<int> f = makeFuture().then(
-  ///     [] { return makeFuture<int>(42); });
-  template <class F>
-  typename std::enable_if<
-    std::is_same<T, void>::value &&
-    isFuture<typename std::result_of<F()>::type>::value,
-    Future<typename std::result_of<F()>::type::value_type> >::type
-  then(F&& func);
-
-  /// Variant where func is an ordinary function (static method, method)
-  ///
-  ///   R doWork(Try<T>&&);
-  ///
-  ///   Future<R> f2 = f1.then(doWork);
-  ///
-  /// or
-  ///
-  ///   struct Worker {
-  ///     static R doWork(Try<T>&&); }
-  ///
-  ///   Future<R> f2 = f1.then(&Worker::doWork);
-  template <class = T, class R = std::nullptr_t>
-  typename std::enable_if<!isFuture<R>::value, Future<R>>::type
-  inline then(R(*func)(Try<T>&&)) {
-    return then([func](Try<T>&& t) {
-      return (*func)(std::move(t));
-    });
-  }
-
-  /// Variant where func returns a Future<R> instead of a R. e.g.
-  ///
-  ///   struct Worker {
-  ///     Future<R> doWork(Try<T>&&); }
-  ///
-  ///   Future<R> f2 = f1.then(&Worker::doWork);
-  template <class = T, class R = std::nullptr_t>
-  typename std::enable_if<isFuture<R>::value, R>::type
-  inline then(R(*func)(Try<T>&&)) {
-    return then([func](Try<T>&& t) {
-      return (*func)(std::move(t));
-    });
-  }
-
-  /// Variant where func is an member function
-  ///
-  ///   struct Worker {
-  ///     R doWork(Try<T>&&); }
-  ///
-  ///   Worker *w;
-  ///   Future<R> f2 = f1.then(w, &Worker::doWork);
-  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
-  typename std::enable_if<!isFuture<R>::value, Future<R>>::type
-  inline then(Caller *instance, R(Caller::*func)(Try<T>&&)) {
-    return then([instance, func](Try<T>&& t) {
-      return (instance->*func)(std::move(t));
-    });
-  }
-
-  // Same as above, but func takes void instead of Try<void>&&
-  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
-  typename std::enable_if<
-      std::is_same<T, void>::value && !isFuture<R>::value, Future<R>>::type
-  inline then(Caller *instance, R(Caller::*func)()) {
-    return then([instance, func]() {
-      return (instance->*func)();
-    });
-  }
-
-  // Same as above, but func takes T&& instead of Try<T>&&
-  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
-  typename std::enable_if<
-      !std::is_same<T, void>::value && !isFuture<R>::value, Future<R>>::type
-  inline then(
-      Caller *instance,
-      R(Caller::*func)(typename detail::AliasIfVoid<T>::type&&)) {
-    return then([instance, func](T&& t) {
-      return (instance->*func)(std::move(t));
-    });
-  }
-
-  /// Variant where func returns a Future<R> instead of a R. e.g.
-  ///
-  ///   struct Worker {
-  ///     Future<R> doWork(Try<T>&&); }
-  ///
-  ///   Worker *w;
-  ///   Future<R> f2 = f1.then(w, &Worker::doWork);
-  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
-  typename std::enable_if<isFuture<R>::value, R>::type
-  inline then(Caller *instance, R(Caller::*func)(Try<T>&&)) {
-    return then([instance, func](Try<T>&& t) {
-      return (instance->*func)(std::move(t));
-    });
-  }
-
-  // Same as above, but func takes void instead of Try<void>&&
-  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
-  typename std::enable_if<
-      std::is_same<T, void>::value && isFuture<R>::value, R>::type
-  inline then(Caller *instance, R(Caller::*func)()) {
-    return then([instance, func]() {
-      return (instance->*func)();
-    });
-  }
-
-  // Same as above, but func takes T&& instead of Try<T>&&
-  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
-  typename std::enable_if<
-      !std::is_same<T, void>::value && isFuture<R>::value, R>::type
-  inline then(
-      Caller *instance,
-      R(Caller::*func)(typename detail::AliasIfVoid<T>::type&&)) {
-    return then([instance, func](T&& t) {
-      return (instance->*func)(std::move(t));
-    });
-  }
-
-  /// Convenience method for ignoring the value and creating a Future<void>.
-  /// Exceptions still propagate.
-  Future<void> then();
-
-  /// This is not the method you're looking for.
-  ///
-  /// This needs to be public because it's used by make* and when*, and it's
-  /// not worth listing all those and their fancy template signatures as
-  /// friends. But it's not for public consumption.
-  template <class F>
-  void setCallback_(F&& func);
-
-  /// A Future's callback is executed when all three of these conditions have
-  /// become true: it has a value (set by the Promise), it has a callback (set
-  /// by then), and it is active (active by default).
-  ///
-  /// Inactive Futures will activate upon destruction.
-  Future<T>& activate() & {
-    core_->activate();
-    return *this;
-  }
-  Future<T>& deactivate() & {
-    core_->deactivate();
-    return *this;
-  }
-  Future<T> activate() && {
-    core_->activate();
-    return std::move(*this);
-  }
-  Future<T> deactivate() && {
-    core_->deactivate();
-    return std::move(*this);
-  }
-
-  bool isActive() {
-    return core_->isActive();
-  }
-
-  template <class E>
-  void raise(E&& exception) {
-    raise(std::make_exception_ptr(std::forward<E>(exception)));
-  }
-
-  /// Raise an interrupt. If the promise holder has an interrupt
-  /// handler it will be called and potentially stop asynchronous work from
-  /// being done. This is advisory only - a promise holder may not set an
-  /// interrupt handler, or may do anything including ignore. But, if you know
-  /// your future supports this the most likely result is stopping or
-  /// preventing the asynchronous operation (if in time), and the promise
-  /// holder setting an exception on the future. (That may happen
-  /// asynchronously, of course.)
-  void raise(std::exception_ptr interrupt);
-
-  void cancel() {
-    raise(FutureCancellation());
-  }
-
- private:
-  typedef detail::Core<T>* corePtr;
-
-  // shared core state object
-  corePtr core_;
-
-  explicit
-  Future(corePtr obj) : core_(obj) {}
-
-  void detach();
-
-  void throwIfInvalid() const;
-
-  friend class Promise<T>;
-};
-
-/**
-  Make a completed Future by moving in a value. e.g.
-
-    string foo = "foo";
-    auto f = makeFuture(std::move(foo));
-
-  or
-
-    auto f = makeFuture<string>("foo");
-*/
-template <class T>
-Future<typename std::decay<T>::type> makeFuture(T&& t);
-
-/** Make a completed void Future. */
-Future<void> makeFuture();
-
-/** Make a completed Future by executing a function. If the function throws
-  we capture the exception, otherwise we capture the result. */
-template <class F>
-auto makeFutureTry(
-  F&& func,
-  typename std::enable_if<
-    !std::is_reference<F>::value, bool>::type sdf = false)
-  -> Future<decltype(func())>;
-
-template <class F>
-auto makeFutureTry(
-  F const& func)
-  -> Future<decltype(func())>;
-
-/// Make a failed Future from an exception_ptr.
-/// Because the Future's type cannot be inferred you have to specify it, e.g.
-///
-///   auto f = makeFuture<string>(std::current_exception());
-template <class T>
-Future<T> makeFuture(std::exception_ptr const& e);
-
-/** Make a Future from an exception type E that can be passed to
-  std::make_exception_ptr(). */
-template <class T, class E>
-typename std::enable_if<std::is_base_of<std::exception, E>::value,
-                        Future<T>>::type
-makeFuture(E const& e);
-
-/** Make a Future out of a Try */
-template <class T>
-Future<T> makeFuture(Try<T>&& t);
-
-/*
- * Return a new Future that will call back on the given Executor.
- * This is just syntactic sugar for makeFuture().via(executor)
- *
- * @param executor the Executor to call back on
- *
- * @returns a void Future that will call back on the given executor
- */
-template <typename Executor>
-Future<void> via(Executor* executor);
-
-/** When all the input Futures complete, the returned Future will complete.
-  Errors do not cause early termination; this Future will always succeed
-  after all its Futures have finished (whether successfully or with an
-  error).
-
-  The Futures are moved in, so your copies are invalid. If you need to
-  chain further from these Futures, use the variant with an output iterator.
-
-  XXX is this still true?
-  This function is thread-safe for Futures running on different threads.
-
-  The return type for Future<T> input is a Future<std::vector<Try<T>>>
-  */
-template <class InputIterator>
-Future<std::vector<Try<
-  typename std::iterator_traits<InputIterator>::value_type::value_type>>>
-whenAll(InputIterator first, InputIterator last);
-
-/// This version takes a varying number of Futures instead of an iterator.
-/// The return type for (Future<T1>, Future<T2>, ...) input
-/// is a Future<std::tuple<Try<T1>, Try<T2>, ...>>.
-/// The Futures are moved in, so your copies are invalid.
-template <typename... Fs>
-typename detail::VariadicContext<
-  typename std::decay<Fs>::type::value_type...>::type
-whenAll(Fs&&... fs);
-
-/** The result is a pair of the index of the first Future to complete and
-  the Try. If multiple Futures complete at the same time (or are already
-  complete when passed in), the "winner" is chosen non-deterministically.
-
-  This function is thread-safe for Futures running on different threads.
-  */
-template <class InputIterator>
-Future<std::pair<
-  size_t,
-  Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
-whenAny(InputIterator first, InputIterator last);
-
-/** when n Futures have completed, the Future completes with a vector of
-  the index and Try of those n Futures (the indices refer to the original
-  order, but the result vector will be in an arbitrary order)
-
-  Not thread safe.
-  */
-template <class InputIterator>
-Future<std::vector<std::pair<
-  size_t,
-  Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>
-whenN(InputIterator first, InputIterator last, size_t n);
-
-/** Wait for the given future to complete on a semaphore. Returns a completed
- * future containing the result.
- *
- * NB if the promise for the future would be fulfilled in the same thread that
- * you call this, it will deadlock.
- */
-template <class T>
-Future<T> waitWithSemaphore(Future<T>&& f);
-
-/** Wait for up to `timeout` for the given future to complete. Returns a future
- * which may or may not be completed depending whether the given future
- * completed in time
- *
- * Note: each call to this starts a (short-lived) thread and allocates memory.
- */
-template <typename T, class Duration>
-Future<T> waitWithSemaphore(Future<T>&& f, Duration timeout);
-
-}} // folly::wangle
-
-#include <folly/wangle/Future-inl.h>
diff --git a/folly/wangle/InlineExecutor.cpp b/folly/wangle/InlineExecutor.cpp
deleted file mode 100644 (file)
index 1ef2f06..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
diff --git a/folly/wangle/InlineExecutor.h b/folly/wangle/InlineExecutor.h
deleted file mode 100644 (file)
index e692408..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <folly/Executor.h>
-
-namespace folly { namespace wangle {
-
-  /// When work is "queued", execute it immediately inline.
-  /// Usually when you think you want this, you actually want a
-  /// QueuedImmediateExecutor.
-  class InlineExecutor : public Executor {
-   public:
-    void add(Func f) override {
-      f();
-    }
-  };
-
-}}
diff --git a/folly/wangle/ManualExecutor.cpp b/folly/wangle/ManualExecutor.cpp
deleted file mode 100644 (file)
index 4a2ee3a..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/wangle/ManualExecutor.h>
-
-#include <string.h>
-#include <string>
-#include <tuple>
-
-#include <stdexcept>
-
-namespace folly { namespace wangle {
-
-ManualExecutor::ManualExecutor() {
-  if (sem_init(&sem_, 0, 0) == -1) {
-    throw std::runtime_error(std::string("sem_init: ") + strerror(errno));
-  }
-}
-
-void ManualExecutor::add(Func callback) {
-  std::lock_guard<std::mutex> lock(lock_);
-  funcs_.push(std::move(callback));
-  sem_post(&sem_);
-}
-
-size_t ManualExecutor::run() {
-  size_t count;
-  size_t n;
-  Func func;
-
-  {
-    std::lock_guard<std::mutex> lock(lock_);
-
-    while (!scheduledFuncs_.empty()) {
-      auto& sf = scheduledFuncs_.top();
-      if (sf.time > now_)
-        break;
-      funcs_.push(sf.func);
-      scheduledFuncs_.pop();
-    }
-
-    n = funcs_.size();
-  }
-
-  for (count = 0; count < n; count++) {
-    {
-      std::lock_guard<std::mutex> lock(lock_);
-      if (funcs_.empty()) {
-        break;
-      }
-
-      // Balance the semaphore so it doesn't grow without bound
-      // if nobody is calling wait().
-      // This may fail (with EAGAIN), that's fine.
-      sem_trywait(&sem_);
-
-      func = std::move(funcs_.front());
-      funcs_.pop();
-    }
-    func();
-  }
-
-  return count;
-}
-
-void ManualExecutor::wait() {
-  while (true) {
-    {
-      std::lock_guard<std::mutex> lock(lock_);
-      if (!funcs_.empty())
-        break;
-    }
-
-    auto ret = sem_wait(&sem_);
-    if (ret == 0) {
-      break;
-    }
-    if (errno != EINVAL) {
-      throw std::runtime_error(std::string("sem_wait: ") + strerror(errno));
-    }
-  }
-}
-
-void ManualExecutor::advanceTo(TimePoint const& t) {
-  if (t > now_) {
-    now_ = t;
-  }
-  run();
-}
-
-}} // namespace
diff --git a/folly/wangle/ManualExecutor.h b/folly/wangle/ManualExecutor.h
deleted file mode 100644 (file)
index 329f9fc..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <folly/wangle/ScheduledExecutor.h>
-#include <semaphore.h>
-#include <memory>
-#include <mutex>
-#include <queue>
-#include <cstdio>
-
-namespace folly { namespace wangle {
-  /// A ManualExecutor only does work when you turn the crank, by calling
-  /// run() or indirectly with makeProgress() or waitFor().
-  ///
-  /// The clock for a manual executor starts at 0 and advances only when you
-  /// ask it to. i.e. time is also under manual control.
-  ///
-  /// NB No attempt has been made to make anything other than add and schedule
-  /// threadsafe.
-  class ManualExecutor : public ScheduledExecutor {
-   public:
-    ManualExecutor();
-
-    void add(Func) override;
-
-    /// Do work. Returns the number of functions that were executed (maybe 0).
-    /// Non-blocking, in the sense that we don't wait for work (we can't
-    /// control whether one of the functions blocks).
-    /// This is stable, it will not chase an ever-increasing tail of work.
-    /// This also means, there may be more work available to perform at the
-    /// moment that this returns.
-    size_t run();
-
-    /// Wait for work to do.
-    void wait();
-
-    /// Wait for work to do, and do it.
-    void makeProgress() {
-      wait();
-      run();
-    }
-
-    /// makeProgress until this Future is ready.
-    template <class F> void waitFor(F const& f) {
-      // TODO(5427828)
-#if 0
-      while (!f.isReady())
-        makeProgress();
-#else
-      while (!f.isReady()) {
-        run();
-      }
-#endif
-
-    }
-
-    virtual void scheduleAt(Func&& f, TimePoint const& t) override {
-      std::lock_guard<std::mutex> lock(lock_);
-      scheduledFuncs_.emplace(t, std::move(f));
-      sem_post(&sem_);
-    }
-
-    /// Advance the clock. The clock never advances on its own.
-    /// Advancing the clock causes some work to be done, if work is available
-    /// to do (perhaps newly available because of the advanced clock).
-    /// If dur is <= 0 this is a noop.
-    void advance(Duration const& dur) {
-      advanceTo(now_ + dur);
-    }
-
-    /// Advance the clock to this absolute time. If t is <= now(),
-    /// this is a noop.
-    void advanceTo(TimePoint const& t);
-
-    TimePoint now() override { return now_; }
-
-   private:
-    std::mutex lock_;
-    std::queue<Func> funcs_;
-    sem_t sem_;
-
-    // helper class to enable ordering of scheduled events in the priority
-    // queue
-    struct ScheduledFunc {
-      TimePoint time;
-      size_t ordinal;
-      Func func;
-
-      ScheduledFunc(TimePoint const& t, Func&& f)
-        : time(t), func(std::move(f))
-      {
-        static size_t seq = 0;
-        ordinal = seq++;
-      }
-
-      bool operator<(ScheduledFunc const& b) const {
-        if (time == b.time)
-          return ordinal < b.ordinal;
-        return time < b.time;
-      }
-    };
-    std::priority_queue<ScheduledFunc> scheduledFuncs_;
-    TimePoint now_ = now_.min();
-  };
-
-}}
diff --git a/folly/wangle/OpaqueCallbackShunt.h b/folly/wangle/OpaqueCallbackShunt.h
deleted file mode 100644 (file)
index 6cd325e..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/wangle/Promise.h>
-
-namespace folly { namespace wangle {
-
-/// These classes help you wrap an existing C style callback function
-/// into a Future.
-///
-///   void legacy_send_async(..., void (*cb)(void*), void*);
-///
-///   Future<T> wrappedSendAsync(T&& obj) {
-///     auto handle = new OpaqueCallbackShunt<T>(obj);
-///     auto future = handle->promise_.getFuture();
-///     legacy_send_async(..., OpaqueCallbackShunt<T>::callback, handle)
-///     return future;
-///   }
-///
-/// If the legacy function doesn't conform to void (*cb)(void*), use a lambda:
-///
-///   auto cb = [](t1*, t2*, void* arg) {
-///     OpaqueCallbackShunt<T>::callback(arg);
-///   };
-///   legacy_send_async(..., cb, handle);
-
-template <typename T>
-class OpaqueCallbackShunt {
-public:
-  explicit OpaqueCallbackShunt(T&& obj)
-    : obj_(std::move(obj)) { }
-  static void callback(void* arg) {
-    std::unique_ptr<OpaqueCallbackShunt<T>> handle(
-      static_cast<OpaqueCallbackShunt<T>*>(arg));
-    handle->promise_.setValue(std::move(handle->obj_));
-  }
-  folly::wangle::Promise<T> promise_;
-private:
-  T obj_;
-};
-
-}} // folly::wangle
diff --git a/folly/wangle/Promise-inl.h b/folly/wangle/Promise-inl.h
deleted file mode 100644 (file)
index 68d6964..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <atomic>
-#include <thread>
-
-#include <folly/wangle/WangleException.h>
-#include <folly/wangle/detail/Core.h>
-
-namespace folly { namespace wangle {
-
-template <class T>
-Promise<T>::Promise() : retrieved_(false), core_(new detail::Core<T>())
-{}
-
-template <class T>
-Promise<T>::Promise(Promise<T>&& other) : core_(nullptr) {
-  *this = std::move(other);
-}
-
-template <class T>
-Promise<T>& Promise<T>::operator=(Promise<T>&& other) {
-  std::swap(core_, other.core_);
-  std::swap(retrieved_, other.retrieved_);
-  return *this;
-}
-
-template <class T>
-void Promise<T>::throwIfFulfilled() {
-  if (!core_)
-    throw NoState();
-  if (core_->ready())
-    throw PromiseAlreadySatisfied();
-}
-
-template <class T>
-void Promise<T>::throwIfRetrieved() {
-  if (retrieved_)
-    throw FutureAlreadyRetrieved();
-}
-
-template <class T>
-Promise<T>::~Promise() {
-  detach();
-}
-
-template <class T>
-void Promise<T>::detach() {
-  if (core_) {
-    if (!retrieved_)
-      core_->detachFuture();
-    core_->detachPromise();
-    core_ = nullptr;
-  }
-}
-
-template <class T>
-Future<T> Promise<T>::getFuture() {
-  throwIfRetrieved();
-  retrieved_ = true;
-  return Future<T>(core_);
-}
-
-template <class T>
-template <class E>
-void Promise<T>::setException(E const& e) {
-  setException(std::make_exception_ptr<E>(e));
-}
-
-template <class T>
-void Promise<T>::setException(std::exception_ptr const& e) {
-  throwIfFulfilled();
-  core_->setResult(Try<T>(e));
-}
-
-template <class T>
-void Promise<T>::setInterruptHandler(
-  std::function<void(std::exception_ptr const&)> fn) {
-  core_->setInterruptHandler(std::move(fn));
-}
-
-template <class T>
-void Promise<T>::fulfilTry(Try<T>&& t) {
-  throwIfFulfilled();
-  core_->setResult(std::move(t));
-}
-
-template <class T>
-template <class M>
-void Promise<T>::setValue(M&& v) {
-  static_assert(!std::is_same<T, void>::value,
-                "Use setValue() instead");
-
-  fulfilTry(Try<T>(std::forward<M>(v)));
-}
-
-template <class T>
-void Promise<T>::setValue() {
-  static_assert(std::is_same<T, void>::value,
-                "Use setValue(value) instead");
-
-  fulfilTry(Try<void>());
-}
-
-template <class T>
-template <class F>
-void Promise<T>::fulfil(F&& func) {
-  throwIfFulfilled();
-  fulfilTry(makeTryFunction(std::forward<F>(func)));
-}
-
-}}
diff --git a/folly/wangle/Promise.h b/folly/wangle/Promise.h
deleted file mode 100644 (file)
index 7442e45..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/wangle/Try.h>
-
-namespace folly { namespace wangle {
-
-// forward declaration
-template <class T> class Future;
-
-template <class T>
-class Promise {
-public:
-  Promise();
-  ~Promise();
-
-  // not copyable
-  Promise(Promise const&) = delete;
-  Promise& operator=(Promise const&) = delete;
-
-  // movable
-  Promise(Promise<T>&&);
-  Promise& operator=(Promise<T>&&);
-
-  /** Return a Future tied to the shared core state. This can be called only
-    once, thereafter Future already retrieved exception will be raised. */
-  Future<T> getFuture();
-
-  /** Fulfil the Promise with an exception_ptr, e.g.
-    try {
-      ...
-    } catch (...) {
-      p.setException(std::current_exception());
-    }
-    */
-  void setException(std::exception_ptr const&);
-
-  /** Fulfil the Promise with an exception type E, which can be passed to
-    std::make_exception_ptr(). Useful for originating exceptions. If you
-    caught an exception the exception_ptr form is more appropriate.
-    */
-  template <class E> void setException(E const&);
-
-  /// Set an interrupt handler to handle interrupts. See the documentation for
-  /// Future::raise(). Your handler can do whatever it wants, but if you
-  /// bother to set one then you probably will want to fulfil the promise with
-  /// an exception (or special value) indicating how the interrupt was
-  /// handled.
-  void setInterruptHandler(std::function<void(std::exception_ptr const&)>);
-
-  /** Fulfil this Promise (only for Promise<void>) */
-  void setValue();
-
-  /** Set the value (use perfect forwarding for both move and copy) */
-  template <class M>
-  void setValue(M&& value);
-
-  void fulfilTry(Try<T>&& t);
-
-  /** Fulfil this Promise with the result of a function that takes no
-    arguments and returns something implicitly convertible to T.
-    Captures exceptions. e.g.
-
-    p.fulfil([] { do something that may throw; return a T; });
-  */
-  template <class F>
-  void fulfil(F&& func);
-
-private:
-  typedef typename Future<T>::corePtr corePtr;
-
-  // Whether the Future has been retrieved (a one-time operation).
-  bool retrieved_;
-
-  // shared core state object
-  corePtr core_;
-
-  void throwIfFulfilled();
-  void throwIfRetrieved();
-  void detach();
-};
-
-}}
-
-#include <folly/wangle/Future.h>
-#include <folly/wangle/Promise-inl.h>
diff --git a/folly/wangle/QueuedImmediateExecutor.cpp b/folly/wangle/QueuedImmediateExecutor.cpp
deleted file mode 100644 (file)
index 739ba5e..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/wangle/QueuedImmediateExecutor.h>
-#include <folly/ThreadLocal.h>
-#include <queue>
-
-namespace folly { namespace wangle {
-
-void QueuedImmediateExecutor::add(Func callback) {
-  thread_local std::queue<Func> q;
-
-  if (q.empty()) {
-    q.push(std::move(callback));
-    while (!q.empty()) {
-      q.front()();
-      q.pop();
-    }
-  } else {
-    q.push(callback);
-  }
-}
-
-}} // namespace
diff --git a/folly/wangle/QueuedImmediateExecutor.h b/folly/wangle/QueuedImmediateExecutor.h
deleted file mode 100644 (file)
index a82c32d..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/Executor.h>
-
-namespace folly { namespace wangle {
-
-/**
- * Runs inline like InlineExecutor, but with a queue so that any tasks added
- * to this executor by one of its own callbacks will be queued instead of
- * executed inline (nested). This is usually better behavior than Inline.
- */
-class QueuedImmediateExecutor : public Executor {
- public:
-  void add(Func) override;
-};
-
-}} // namespace
diff --git a/folly/wangle/README.md b/folly/wangle/README.md
deleted file mode 100644 (file)
index cb88fe3..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-# Wangle
-Wangle is a framework for expressing asynchronous code in C++ using the Future pattern.
-
-**wan•gle** |ˈwaNGgÉ™l| informal  
-*verb*  
-Obtain (something that is desired) by persuading others to comply or by manipulating events.
-
-*noun*  
-A framework for expressing asynchronous control flow in C++, that is composable and easily translated to/from synchronous code.
-
-*synonyms*  
-[Finagle](http://twitter.github.io/finagle/)
-
-Wangle is a futures-based async framework inspired by [Twitter's Finagle](http://twitter.github.io/finagle/) (which is in scala), and (loosely) building upon the existing (but anemic) Futures code found in the C++11 standard ([`std::future`](http://en.cppreference.com/w/cpp/thread/future)) and [`boost::future`](http://www.boost.org/doc/libs/1_53_0/boost/thread/future.hpp) (especially >= 1.53.0). Although inspired by the std::future interface, it is not syntactically drop-in compatible because some ideas didn't translate well enough and we decided to break from the API. But semantically, it should be straightforward to translate from existing std::future code to Wangle.
-
-The primary semantic differences are that Wangle Futures and Promises are not threadsafe; and as does `boost::future`, Wangle supports continuing callbacks (`then()`) and there are helper methods `whenAll()` and `whenAny()` which are important compositional building blocks.
-
-## Brief Synopsis
-
-```C++
-#include <folly/wangle/Future.h>
-using namespace folly::wangle;
-using namespace std;
-
-void foo(int x) {
-  // do something with x
-  cout << "foo(" << x << ")" << endl;
-}
-
-// ...
-
-  cout << "making Promise" << endl;
-  Promise<int> p;
-  Future<int> f = p.getFuture();
-  f.then(
-    [](Try<int>&& t) {
-      foo(t.value());
-    });
-  cout << "Future chain made" << endl;
-
-// ... now perhaps in another event callback
-
-  cout << "fulfilling Promise" << endl;
-  p.setValue(42);
-  cout << "Promise fulfilled" << endl;
-```
-
-This would print:
-  
-```
-making Promise
-Future chain made
-fulfilling Promise
-foo(42)
-Promise fulfilled
-```
-
-## User Guide
-
-Let's begin with an example. Consider a simplified Memcache client class with this interface:
-
-```C++
-class MemcacheClient {
- public:
-  struct GetReply {
-    enum class Result {
-      FOUND,
-      NOT_FOUND,
-      SERVER_ERROR,
-    };
-
-    Result result;
-    // The value when result is FOUND,
-    // The error message when result is SERVER_ERROR or CLIENT_ERROR
-    // undefined otherwise
-    std::string value;
-  };
-
-  GetReply get(std::string key);
-};
-```
-
-This API is synchronous, i.e. when you call `get()` you have to wait for the result. This is very simple, but unfortunately it is also very easy to write very slow code using synchronous APIs.
-
-Now, consider this traditional asynchronous signature for `get()`:
-
-```C++
-int get(std::string key, std::function<void(GetReply)> callback);
-```
-
-When you call `get()`, your asynchronous operation begins and when it finishes your callback will be called with the result. (Unless something goes drastically wrong and you get an error code from `get()`.) Very performant code can be written with an API like this, but for nontrivial applications the code descends into a special kind of spaghetti code affectionately referred to as "callback hell".
-
-The Future-based API looks like this:
-
-```C++
-Future<GetReply> get(std::string key);
-```
-
-A `Future<GetReply>` is a placeholder for the `GetReply` that we will eventually get. A Future usually starts life out "unfulfilled", or incomplete, i.e.:
-
-```C++
-fut.isReady() == false
-fut.value()  // will throw an exception because the Future is not ready
-```
-
-At some point in the future, the Future will have been fulfilled, and we can access its value.
-
-```C++
-fut.isReady() == true
-GetReply& reply = fut.value();
-```
-
-Futures support exceptions. If something exceptional happened, your Future may represent an exception instead of a value. In that case:
-
-```C++
-fut.isReady() == true
-fut.value() // will rethrow the exception
-```
-
-Just what is exceptional depends on the API. In our example we have chosen not to raise exceptions for `SERVER_ERROR`, but represent this explicitly in the `GetReply` object. On the other hand, an astute Memcache veteran would notice that we left `CLIENT_ERROR` out of `GetReply::Result`, and perhaps a `CLIENT_ERROR` would have been raised as an exception, because `CLIENT_ERROR` means there's a bug in the library and this would be truly exceptional. These decisions are judgement calls by the API designer. The important thing is that exceptional conditions (including and especially spurious exceptions that nobody expects) get captured and can be handled higher up the "stack".
-
-So far we have described a way to initiate an asynchronous operation via an API that returns a Future, and then sometime later after it is fulfilled, we get its value. This is slightly more useful than a synchronous API, but it's not yet ideal. There are two more very important pieces to the puzzle.
-
-First, we can aggregate Futures, to define a new Future that completes after some or all of the aggregated Futures complete.  Consider two examples: fetching a batch of requests and waiting for all of them, and fetching a group of requests and waiting for only one of them.
-
-```C++
-vector<Future<GetReply>> futs;
-for (auto& key : keys) {
-  futs.push_back(mc.get(key));
-}
-auto all = whenAll(futs.begin(), futs.end());
-
-vector<Future<GetReply>> futs;
-for (auto& key : keys) {
-  futs.push_back(mc.get(key));
-}
-auto any = whenAny(futs.begin(), futs.end());
-```
-
-`all` and `any` are Futures (for the exact type and usage see the header files).  They will be complete when all/one of `futs` are complete, respectively. (There is also `whenN()` for when you need *some*.)
-
-Second, we can attach callbacks to a Future, and chain them together monadically. An example will clarify:
-
-```C++
-Future<GetReply> fut1 = mc.get("foo");
-
-Future<string> fut2 = fut1.then(
-  [](Try<GetReply>&& t) {
-    if (t.value().result == MemcacheClient::GetReply::Result::FOUND)
-      return t.value().value;
-    throw SomeException("No value");
-  });
-
-Future<void> fut3 = fut2.then(
-  [](Try<string>&& t) {
-    try {
-      cout << t.value() << endl;
-    } catch (std::exception const& e) {
-      cerr << e.what() << endl;
-    }
-  });
-```
-
-That example is a little contrived but the idea is that you can transform a result from one type to another, potentially in a chain, and unhandled errors propagate. Of course, the intermediate variables are optional. `Try<T>` is the object wrapper that supports both value and exception.
-
-Using `then` to add callbacks is idiomatic. It brings all the code into one place, which avoids callback hell.
-
-Up to this point we have skirted around the matter of waiting for Futures. You may never need to wait for a Future, because your code is event-driven and all follow-up action happens in a then-block. But if want to have a batch workflow, where you initiate a batch of asynchronous operations and then wait for them all to finish at a synchronization point, then you will want to wait for a Future.
-
-Other future frameworks like Finagle and std::future/boost::future, give you the ability to wait directly on a Future, by calling `fut.wait()` (naturally enough). Wangle has diverged from this pattern because we don't want to be in the business of dictating how your thread waits. We may work out something that we feel is sufficiently general, in the meantime adapt this spin loop to however your thread should wait:
-
-  while (!f.isReady()) {}
-
-(Hint: you might want to use an event loop or a semaphore or something. You probably don't want to just spin like this.)
-
-Wangle is partially threadsafe. A Promise or Future can migrate between threads as long as there's a full memory barrier of some sort. `Future::then` and `Promise::setValue` (and all variants that boil down to those two calls) can be called from different threads. BUT, be warned that you might be surprised about which thread your callback executes on. Let's consider an example.
-
-```C++
-// Thread A
-Promise<void> p;
-auto f = p.getFuture();
-
-// Thread B
-f.then(x).then(y).then(z);
-
-// Thread A
-p.setValue();
-```
-
-This is legal and technically threadsafe. However, it is important to realize that you do not know in which thread `x`, `y`, and/or `z` will execute. Maybe they will execute in Thread A when `p.setValue()` is called. Or, maybe they will execute in Thread B when `f.then` is called. Or, maybe `x` will execute in Thread B, but `y` and/or `z` will execute in Thread A. There's a race between `setValue` and `then`—whichever runs last will execute the callback. The only guarantee is that one of them will run the callback.
-
-Naturally, you will want some control over which thread executes callbacks. We have a few mechanisms to help.
-
-The first and most useful is `via`, which passes execution through an `Executor`, which usually has the effect of running the callback in a new thread.
-```C++
-aFuture
-  .then(x)
-  .via(e1).then(y1).then(y2)
-  .via(e2).then(z);
-```
-`x` will execute in the current thread. `y1` and `y2` will execute in the thread on the other side of `e1`, and `z` will execute in the thread on the other side of `e2`. `y1` and `y2` will execute on the same thread, whichever thread that is. If `e1` and `e2` execute in different threads than the current thread, then the final callback does not happen in the current thread. If you want to get back to the current thread, you need to get there via an executor.
-
-This works because `via` returns a deactivated ("cold") Future, which blocks the propagation of callbacks until it is activated. Activation happens either explicitly (`activate`) or implicitly when the Future returned by `via` is destructed. In this example, there is no ambiguity about in which context any of the callbacks happen (including `y2`), because propagation is blocked at the `via` callsites until after everything is wired up (temporaries are destructed after the calls to `then` have completed).
-
-You can still have a race after `via` if you break it into multiple statements, e.g. in this counterexample:
-```C++
-f = f.via(e1).then(y1).then(y2); // nothing racy here
-f2.then(y3); // racy
-```
-
-## You make me Promises, Promises
-
-If you are wrapping an asynchronous operation, or providing an asynchronous API to users, then you will want to make Promises. Every Future has a corresponding Promise (except Futures that spring into existence already completed, with `makeFuture()`). Promises are simple, you make one, you extract the Future, and you fulfil it with a value or an exception. Example:
-
-```C++
-Promise<int> p;
-Future<int> f = p.getFuture();
-
-f.isReady() == false
-
-p.setValue(42);
-
-f.isReady() == true
-f.value() == 42
-```
-
-and an exception example:
-
-```C++
-Promise<int> p;
-Future<int> f = p.getFuture();
-
-f.isReady() == false
-
-p.setException(std::runtime_error("Fail"));
-
-f.isReady() == true
-f.value() // throws the exception
-```
-
-It's good practice to use fulfil which takes a function and automatically captures exceptions, e.g.
-
-```C++
-Promise<int> p;
-p.fulfil([]{
-  try {
-    // do stuff that may throw
-    return 42;
-  } catch (MySpecialException const& e) {
-    // handle it
-    return 7;
-  }
-  // Any exceptions that we didn't catch, will be caught for us
-});
-```
-
-## FAQ
-
-### Why not use std::future?
-No callback support.
-See also http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3428.pdf
-
-### Why not use boost::future?
-- 1.53 is brand new, and not in fbcode
-- It's still a bit buggy/bleeding-edge
-- They haven't fleshed out the threading model very well yet, e.g. every single `then` currently spawns a new thread unless you explicitly ask it to work on this thread only, and there is no support for executors yet.
-
-### Why use heap-allocated shared state? Why is Promise not a subclass of Future?
-C++. It boils down to wanting to return a Future by value for performance (move semantics and compiler optimizations), and programmer sanity, and needing a reference to the shared state by both the user (which holds the Future) and the asynchronous operation (which holds the Promise), and allowing either to go out of scope.
-
-### What about proper continuations? Futures suck.
-People mean two things here, they either mean using continuations (as in CSP) or they mean using generators which require continuations. It's important to know those are two distinct questions, but in our context the answer is the same because continuations are a prerequisite for generators.
-
-C++ doesn't directly support continuations very well. But there are some ways to do them in C/C++ that rely on some rather low-level facilities like `setjmp` and `longjmp` (among others). So yes, they are possible (cf. [Mordor](https://github.com/ccutrer/mordor)).
-
-The tradeoff is memory. Each continuation has a stack, and that stack is usually fixed-size and has to be big enough to support whatever ordinary computation you might want to do on it. So each living continuation requires a relatively large amount of memory. If you know the number of continuations will be small, this might be a good fit. In particular, it might be faster and the code might read cleaner.
-
-Wangle takes the middle road between callback hell and continuations, one which has been trodden and proved useful in other languages. It doesn't claim to be the best model for all situations. Use your tools wisely.
diff --git a/folly/wangle/ScheduledExecutor.h b/folly/wangle/ScheduledExecutor.h
deleted file mode 100644 (file)
index 94850c2..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/Executor.h>
-#include <chrono>
-#include <memory>
-#include <stdexcept>
-
-namespace folly { namespace wangle {
-  // An executor that supports timed scheduling. Like RxScheduler.
-  class ScheduledExecutor : public Executor {
-   public:
-     // Reality is that better than millisecond resolution is very hard to
-     // achieve. However, we reserve the right to be incredible.
-     typedef std::chrono::microseconds Duration;
-     typedef std::chrono::steady_clock::time_point TimePoint;
-
-     virtual ~ScheduledExecutor() = default;
-
-     virtual void add(Func) override = 0;
-
-     /// Alias for add() (for Rx consistency)
-     void schedule(Func&& a) { add(std::move(a)); }
-
-     /// Schedule a Func to be executed after dur time has elapsed
-     /// Expect millisecond resolution at best.
-     void schedule(Func&& a, Duration const& dur) {
-       scheduleAt(std::move(a), now() + dur);
-     }
-
-     /// Schedule a Func to be executed at time t, or as soon afterward as
-     /// possible. Expect millisecond resolution at best. Must be threadsafe.
-     virtual void scheduleAt(Func&& a, TimePoint const& t) {
-       throw std::logic_error("unimplemented");
-     }
-
-     /// Get this executor's notion of time. Must be threadsafe.
-     virtual TimePoint now() {
-       return std::chrono::steady_clock::now();
-     }
-  };
-}}
diff --git a/folly/wangle/Try-inl.h b/folly/wangle/Try-inl.h
deleted file mode 100644 (file)
index 935f54b..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdexcept>
-
-#include <folly/wangle/WangleException.h>
-
-namespace folly { namespace wangle {
-
-template <class T>
-Try<T>::Try(Try<T>&& t) : contains_(t.contains_) {
-  if (contains_ == Contains::VALUE) {
-    new (&value_)T(std::move(t.value_));
-  } else if (contains_ == Contains::EXCEPTION) {
-    new (&e_)std::exception_ptr(t.e_);
-  }
-}
-
-template <class T>
-Try<T>& Try<T>::operator=(Try<T>&& t) {
-  this->~Try();
-  contains_ = t.contains_;
-  if (contains_ == Contains::VALUE) {
-    new (&value_)T(std::move(t.value_));
-  } else if (contains_ == Contains::EXCEPTION) {
-    new (&e_)std::exception_ptr(t.e_);
-  }
-  return *this;
-}
-
-template <class T>
-Try<T>::~Try() {
-  if (contains_ == Contains::VALUE) {
-    value_.~T();
-  } else if (contains_ == Contains::EXCEPTION) {
-    e_.~exception_ptr();
-  }
-}
-
-template <class T>
-T& Try<T>::value() {
-  throwIfFailed();
-  return value_;
-}
-
-template <class T>
-const T& Try<T>::value() const {
-  throwIfFailed();
-  return value_;
-}
-
-template <class T>
-void Try<T>::throwIfFailed() const {
-  if (contains_ != Contains::VALUE) {
-    if (contains_ == Contains::EXCEPTION) {
-      std::rethrow_exception(e_);
-    } else {
-      throw UsingUninitializedTry();
-    }
-  }
-}
-
-void Try<void>::throwIfFailed() const {
-  if (!hasValue_) {
-    std::rethrow_exception(e_);
-  }
-}
-
-template <typename T>
-inline T moveFromTry(wangle::Try<T>&& t) {
-  return std::move(t.value());
-}
-
-inline void moveFromTry(wangle::Try<void>&& t) {
-  return t.value();
-}
-
-template <typename F>
-typename std::enable_if<
-  !std::is_same<typename std::result_of<F()>::type, void>::value,
-  Try<typename std::result_of<F()>::type>>::type
-makeTryFunction(F&& f) {
-  typedef typename std::result_of<F()>::type ResultType;
-  try {
-    auto value = f();
-    return Try<ResultType>(std::move(value));
-  } catch (...) {
-    return Try<ResultType>(std::current_exception());
-  }
-}
-
-template <typename F>
-typename std::enable_if<
-  std::is_same<typename std::result_of<F()>::type, void>::value,
-  Try<void>>::type
-makeTryFunction(F&& f) {
-  try {
-    f();
-    return Try<void>();
-  } catch (...) {
-    return Try<void>(std::current_exception());
-  }
-}
-
-}}
diff --git a/folly/wangle/Try.h b/folly/wangle/Try.h
deleted file mode 100644 (file)
index bc7800d..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <type_traits>
-#include <exception>
-#include <algorithm>
-#include <folly/Likely.h>
-#include <folly/wangle/WangleException.h>
-
-namespace folly { namespace wangle {
-
-template <class T>
-class Try {
-  static_assert(!std::is_reference<T>::value,
-                "Try may not be used with reference types");
-
-  enum class Contains {
-    VALUE,
-    EXCEPTION,
-    NOTHING,
-  };
-
- public:
-  typedef T element_type;
-
-  Try() : contains_(Contains::NOTHING) {}
-  explicit Try(const T& v) : contains_(Contains::VALUE), value_(v) {}
-  explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {}
-  explicit Try(std::exception_ptr e) : contains_(Contains::EXCEPTION), e_(e) {}
-
-  // move
-  Try(Try<T>&& t);
-  Try& operator=(Try<T>&& t);
-
-  // no copy
-  Try(const Try<T>& t) = delete;
-  Try& operator=(const Try<T>& t) = delete;
-
-  ~Try();
-
-  T& value();
-  const T& value() const;
-
-  void throwIfFailed() const;
-
-  const T& operator*() const { return value(); }
-        T& operator*()       { return value(); }
-
-  const T* operator->() const { return &value(); }
-        T* operator->()       { return &value(); }
-
-  bool hasValue() const { return contains_ == Contains::VALUE; }
-  bool hasException() const { return contains_ == Contains::EXCEPTION; }
-
-  std::exception_ptr getException() const {
-    if (UNLIKELY(!hasException())) {
-      throw WangleException(
-          "getException(): Try does not contain an exception");
-    }
-    return e_;
-  }
-
- private:
-  Contains contains_;
-  union {
-    T value_;
-    std::exception_ptr e_;
-  };
-};
-
-template <>
-class Try<void> {
- public:
-  Try() : hasValue_(true) {}
-  explicit Try(std::exception_ptr e) : hasValue_(false), e_(e) {}
-
-  void value() const { throwIfFailed(); }
-  void operator*() const { return value(); }
-
-  inline void throwIfFailed() const;
-
-  bool hasValue() const { return hasValue_; }
-  bool hasException() const { return !hasValue_; }
-
-  std::exception_ptr getException() const {
-    if (UNLIKELY(!hasException())) {
-      throw WangleException(
-          "getException(): Try does not contain an exception");
-    }
-    return e_;
-  }
-
- private:
-  bool hasValue_;
-  std::exception_ptr e_;
-};
-
-/**
- * Extracts value from try and returns it. Throws if try contained an exception.
- */
-template <typename T>
-T moveFromTry(wangle::Try<T>&& t);
-
-/**
- * Throws if try contained an exception.
- */
-void moveFromTry(wangle::Try<void>&& t);
-
-/**
- * Constructs Try based on the result of execution of function f (e.g. result
- * or exception).
- */
-template <typename F>
-typename std::enable_if<
-  !std::is_same<typename std::result_of<F()>::type, void>::value,
-  Try<typename std::result_of<F()>::type>>::type
-makeTryFunction(F&& f);
-
-/**
- * makeTryFunction specialization for void functions.
- */
-template <typename F>
-typename std::enable_if<
-  std::is_same<typename std::result_of<F()>::type, void>::value,
-  Try<void>>::type
-makeTryFunction(F&& f);
-
-
-}}
-
-#include <folly/wangle/Try-inl.h>
diff --git a/folly/wangle/WangleException.h b/folly/wangle/WangleException.h
deleted file mode 100644 (file)
index bddf86c..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <exception>
-#include <string>
-
-namespace folly { namespace wangle {
-
-class WangleException : public std::exception {
-
-public:
-
-  explicit WangleException(std::string message_arg)
-    : message(message_arg) {}
-
-  ~WangleException() throw(){}
-
-  virtual const char *what() const throw() {
-    return message.c_str();
-  }
-
-  bool operator==(const WangleException &other) const{
-    return other.message == this->message;
-  }
-
-  bool operator!=(const WangleException &other) const{
-    return !(*this == other);
-  }
-
-  protected:
-    std::string message;
-};
-
-class BrokenPromise : public WangleException {
-  public:
-    explicit BrokenPromise() :
-      WangleException("Broken promise") { }
-};
-
-class NoState : public WangleException {
-  public:
-    explicit NoState() : WangleException("No state") { }
-};
-
-class PromiseAlreadySatisfied : public WangleException {
-  public:
-    explicit PromiseAlreadySatisfied() :
-      WangleException("Promise already satisfied") { }
-};
-
-class FutureNotReady : public WangleException {
-  public:
-    explicit FutureNotReady() :
-      WangleException("Future not ready") { }
-};
-
-class FutureAlreadyRetrieved : public WangleException {
-  public:
-    explicit FutureAlreadyRetrieved () :
-      WangleException("Future already retrieved") { }
-};
-
-class UsingUninitializedTry : public WangleException {
-  public:
-    explicit UsingUninitializedTry() :
-      WangleException("Using unitialized try") { }
-};
-
-class FutureCancellation : public WangleException {
- public:
-  FutureCancellation() : WangleException("Future was cancelled") {}
-};
-
-}}
diff --git a/folly/wangle/detail/Core.h b/folly/wangle/detail/Core.h
deleted file mode 100644 (file)
index 933cd7a..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <atomic>
-#include <mutex>
-#include <stdexcept>
-#include <vector>
-
-#include <folly/Optional.h>
-#include <folly/SmallLocks.h>
-
-#include <folly/wangle/Try.h>
-#include <folly/wangle/Promise.h>
-#include <folly/wangle/Future.h>
-#include <folly/Executor.h>
-#include <folly/wangle/detail/FSM.h>
-
-#include <folly/io/async/Request.h>
-
-namespace folly { namespace wangle { namespace detail {
-
-// As of GCC 4.8.1, the std::function in libstdc++ optimizes only for pointers
-// to functions, using a helper avoids a call to malloc.
-template<typename T>
-void empty_callback(Try<T>&&) { }
-
-enum class State {
-  Waiting,
-  Interruptible,
-  Interrupted,
-  Done,
-};
-
-/** The shared state object for Future and Promise. */
-template<typename T>
-class Core : protected FSM<State> {
- public:
-  // This must be heap-constructed. There's probably a way to enforce that in
-  // code but since this is just internal detail code and I don't know how
-  // off-hand, I'm punting.
-  Core() : FSM<State>(State::Waiting) {}
-  ~Core() {
-    assert(calledBack_);
-    assert(detached_ == 2);
-  }
-
-  // not copyable
-  Core(Core const&) = delete;
-  Core& operator=(Core const&) = delete;
-
-  // not movable (see comment in the implementation of Future::then)
-  Core(Core&&) noexcept = delete;
-  Core& operator=(Core&&) = delete;
-
-  Try<T>& getTry() {
-    if (ready()) {
-      return *result_;
-    } else {
-      throw FutureNotReady();
-    }
-  }
-
-  template <typename F>
-  void setCallback(F func) {
-    auto setCallback_ = [&]{
-      if (callback_) {
-        throw std::logic_error("setCallback called twice");
-      }
-
-      context_ = RequestContext::saveContext();
-      callback_ = std::move(func);
-    };
-
-    FSM_START
-      case State::Waiting:
-      case State::Interruptible:
-      case State::Interrupted:
-        FSM_UPDATE(state, setCallback_);
-        break;
-
-      case State::Done:
-        FSM_UPDATE2(State::Done,
-          setCallback_,
-          [&]{ maybeCallback(); });
-        break;
-    FSM_END
-  }
-
-  void setResult(Try<T>&& t) {
-    FSM_START
-      case State::Waiting:
-      case State::Interruptible:
-      case State::Interrupted:
-        FSM_UPDATE2(State::Done,
-          [&]{ result_ = std::move(t); },
-          [&]{ maybeCallback(); });
-        break;
-
-      case State::Done:
-        throw std::logic_error("setResult called twice");
-    FSM_END
-  }
-
-  bool ready() const {
-    return getState() == State::Done;
-  }
-
-  // Called by a destructing Future
-  void detachFuture() {
-    if (!callback_) {
-      setCallback(empty_callback<T>);
-    }
-    activate();
-    detachOne();
-  }
-
-  // Called by a destructing Promise
-  void detachPromise() {
-    if (!ready()) {
-      setResult(Try<T>(std::make_exception_ptr(BrokenPromise())));
-    }
-    detachOne();
-  }
-
-  void deactivate() {
-    active_ = false;
-  }
-
-  void activate() {
-    active_ = true;
-    if (ready()) {
-      maybeCallback();
-    }
-  }
-
-  bool isActive() { return active_; }
-
-  void setExecutor(Executor* x) {
-    executor_ = x;
-  }
-
-  void raise(std::exception_ptr const& e) {
-    FSM_START
-      case State::Interruptible:
-        FSM_UPDATE2(State::Interrupted,
-          [&]{ interrupt_ = e; },
-          [&]{ interruptHandler_(interrupt_); });
-        break;
-
-      case State::Waiting:
-      case State::Interrupted:
-        FSM_UPDATE(State::Interrupted,
-          [&]{ interrupt_ = e; });
-        break;
-
-      case State::Done:
-        FSM_BREAK
-    FSM_END
-  }
-
-  void setInterruptHandler(std::function<void(std::exception_ptr const&)> fn) {
-    FSM_START
-      case State::Waiting:
-      case State::Interruptible:
-        FSM_UPDATE(State::Interruptible,
-          [&]{ interruptHandler_ = std::move(fn); });
-        break;
-
-      case State::Interrupted:
-        fn(interrupt_);
-        FSM_BREAK
-
-      case State::Done:
-        FSM_BREAK
-    FSM_END
-  }
-
- private:
-  void maybeCallback() {
-    assert(ready());
-    if (isActive() && callback_) {
-      if (!calledBack_.exchange(true)) {
-        // TODO(5306911) we should probably try/catch
-        Executor* x = executor_;
-
-        RequestContext::setContext(context_);
-        if (x) {
-          MoveWrapper<std::function<void(Try<T>&&)>> cb(std::move(callback_));
-          MoveWrapper<folly::Optional<Try<T>>> val(std::move(result_));
-          x->add([cb, val]() mutable { (*cb)(std::move(**val)); });
-        } else {
-          callback_(std::move(*result_));
-        }
-      }
-    }
-  }
-
-  void detachOne() {
-    auto d = ++detached_;
-    assert(d >= 1);
-    assert(d <= 2);
-    if (d == 2) {
-      // we should have already executed the callback with the value
-      assert(calledBack_);
-      delete this;
-    }
-  }
-
-  folly::Optional<Try<T>> result_;
-  std::function<void(Try<T>&&)> callback_;
-  std::shared_ptr<RequestContext> context_{nullptr};
-  std::atomic<bool> calledBack_ {false};
-  std::atomic<unsigned char> detached_ {0};
-  std::atomic<bool> active_ {true};
-  std::atomic<Executor*> executor_ {nullptr};
-  std::exception_ptr interrupt_;
-  std::function<void(std::exception_ptr const&)> interruptHandler_;
-};
-
-template <typename... Ts>
-struct VariadicContext {
-  VariadicContext() : total(0), count(0) {}
-  Promise<std::tuple<Try<Ts>... > > p;
-  std::tuple<Try<Ts>... > results;
-  size_t total;
-  std::atomic<size_t> count;
-  typedef Future<std::tuple<Try<Ts>...>> type;
-};
-
-template <typename... Ts, typename THead, typename... Fs>
-typename std::enable_if<sizeof...(Fs) == 0, void>::type
-whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
-  head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
-    std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
-    if (++ctx->count == ctx->total) {
-      ctx->p.setValue(std::move(ctx->results));
-      delete ctx;
-    }
-  });
-}
-
-template <typename... Ts, typename THead, typename... Fs>
-typename std::enable_if<sizeof...(Fs) != 0, void>::type
-whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
-  head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
-    std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
-    if (++ctx->count == ctx->total) {
-      ctx->p.setValue(std::move(ctx->results));
-      delete ctx;
-    }
-  });
-  // template tail-recursion
-  whenAllVariadicHelper(ctx, std::forward<Fs>(tail)...);
-}
-
-template <typename T>
-struct WhenAllContext {
-  WhenAllContext() : count(0) {}
-  Promise<std::vector<Try<T> > > p;
-  std::vector<Try<T> > results;
-  std::atomic<size_t> count;
-};
-
-template <typename T>
-struct WhenAnyContext {
-  explicit WhenAnyContext(size_t n) : done(false), ref_count(n) {};
-  Promise<std::pair<size_t, Try<T>>> p;
-  std::atomic<bool> done;
-  std::atomic<size_t> ref_count;
-  void decref() {
-    if (--ref_count == 0) {
-      delete this;
-    }
-  }
-};
-
-}}} // namespace
diff --git a/folly/wangle/detail/Dummy.cpp b/folly/wangle/detail/Dummy.cpp
deleted file mode 100644 (file)
index 02a58d4..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// fbbuild is too dumb to know that .h files in the directory affect
-// our project, unless we have a .cpp file in the target, in the same
-// directory.
diff --git a/folly/wangle/detail/FSM.h b/folly/wangle/detail/FSM.h
deleted file mode 100644 (file)
index be4eb8a..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <atomic>
-#include <mutex>
-#include <folly/SmallLocks.h>
-
-namespace folly { namespace wangle { namespace detail {
-
-/// Finite State Machine helper base class.
-/// Inherit from this.
-/// For best results, use an "enum class" for Enum.
-template <class Enum>
-class FSM {
-private:
-  // I am not templatizing this because folly::MicroSpinLock needs to be
-  // zero-initialized (or call init) which isn't generic enough for something
-  // that behaves like std::mutex. :(
-  using Mutex = folly::MicroSpinLock;
-  Mutex mutex_ {0};
-
-  // This might not be necessary for all Enum types, e.g. anything
-  // that is atomically updated in practice on this CPU and there's no risk
-  // of returning a bogus state because of tearing.
-  // An optimization would be to use a static conditional on the Enum type.
-  std::atomic<Enum> state_;
-
-public:
-  explicit FSM(Enum startState) : state_(startState) {}
-
-  Enum getState() const {
-    return state_.load(std::memory_order_relaxed);
-  }
-
-  /// Atomically do a state transition with accompanying action.
-  /// The action will see the old state.
-  /// @returns true on success, false and action unexecuted otherwise
-  template <class F>
-  bool updateState(Enum A, Enum B, F const& action) {
-    std::lock_guard<Mutex> lock(mutex_);
-    if (state_ != A) return false;
-    action();
-    state_ = B;
-    return true;
-  }
-
-  /// Atomically do a state transition with accompanying action. Then do the
-  /// unprotected action without holding the lock. If the atomic transition
-  /// fails, returns false and neither action was executed.
-  ///
-  /// This facilitates code like this:
-  ///   bool done = false;
-  ///   while (!done) {
-  ///     switch (getState()) {
-  ///     case State::Foo:
-  ///       done = updateState(State::Foo, State::Bar,
-  ///           [&]{ /* do protected stuff */ },
-  ///           [&]{ /* do unprotected stuff */});
-  ///       break;
-  ///
-  /// Which reads nicer than code like this:
-  ///   while (true) {
-  ///     switch (getState()) {
-  ///     case State::Foo:
-  ///       if (!updateState(State::Foo, State::Bar,
-  ///           [&]{ /* do protected stuff */ })) {
-  ///         continue;
-  ///       }
-  ///       /* do unprotected stuff */
-  ///       return; // or otherwise break out of the loop
-  ///
-  /// The protected action will see the old state, and the unprotected action
-  /// will see the new state.
-  template <class F1, class F2>
-  bool updateState(Enum A, Enum B,
-                   F1 const& protectedAction, F2 const& unprotectedAction) {
-    bool result = updateState(A, B, protectedAction);
-    if (result) {
-      unprotectedAction();
-    }
-    return result;
-  }
-};
-
-#define FSM_START \
-  {bool done = false; while (!done) { auto state = getState(); switch (state) {
-
-#define FSM_UPDATE2(b, protectedAction, unprotectedAction) \
-    done = updateState(state, (b), (protectedAction), (unprotectedAction));
-
-#define FSM_UPDATE(b, action) FSM_UPDATE2((b), (action), []{})
-
-#define FSM_CASE(a, b, action) \
-  case (a): \
-    FSM_UPDATE((b), (action)); \
-    break;
-
-#define FSM_CASE2(a, b, protectedAction, unprotectedAction) \
-  case (a): \
-    FSM_UPDATE2((b), (protectedAction), (unprotectedAction)); \
-    break;
-
-#define FSM_BREAK done = true; break;
-#define FSM_END }}}
-
-
-}}}
diff --git a/folly/wangle/futures/Deprecated.h b/folly/wangle/futures/Deprecated.h
new file mode 100644 (file)
index 0000000..75937b1
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#define DEPRECATED __attribute__((__deprecated__))
diff --git a/folly/wangle/futures/Future-inl.h b/folly/wangle/futures/Future-inl.h
new file mode 100644 (file)
index 0000000..074571d
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <thread>
+
+#include <folly/wangle/futures/detail/Core.h>
+#include <folly/Baton.h>
+
+namespace folly { namespace wangle {
+
+template <typename T>
+struct isFuture {
+  static const bool value = false;
+};
+
+template <typename T>
+struct isFuture<Future<T> > {
+  static const bool value = true;
+};
+
+template <class T>
+Future<T>::Future(Future<T>&& other) noexcept : core_(nullptr) {
+  *this = std::move(other);
+}
+
+template <class T>
+Future<T>& Future<T>::operator=(Future<T>&& other) {
+  std::swap(core_, other.core_);
+  return *this;
+}
+
+template <class T>
+Future<T>::~Future() {
+  detach();
+}
+
+template <class T>
+void Future<T>::detach() {
+  if (core_) {
+    core_->detachFuture();
+    core_ = nullptr;
+  }
+}
+
+template <class T>
+void Future<T>::throwIfInvalid() const {
+  if (!core_)
+    throw NoState();
+}
+
+template <class T>
+template <class F>
+void Future<T>::setCallback_(F&& func) {
+  throwIfInvalid();
+  core_->setCallback(std::move(func));
+}
+
+// Variant: f.then([](Try<T>&& t){ return t.value(); });
+template <class T>
+template <class F>
+typename std::enable_if<
+  !isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
+  Future<typename std::result_of<F(Try<T>&&)>::type> >::type
+Future<T>::then(F&& func) {
+  typedef typename std::result_of<F(Try<T>&&)>::type B;
+
+  throwIfInvalid();
+
+  // wrap these so we can move them into the lambda
+  folly::MoveWrapper<Promise<B>> p;
+  folly::MoveWrapper<F> funcm(std::forward<F>(func));
+
+  // grab the Future now before we lose our handle on the Promise
+  auto f = p->getFuture();
+
+  /* This is a bit tricky.
+
+     We can't just close over *this in case this Future gets moved. So we
+     make a new dummy Future. We could figure out something more
+     sophisticated that avoids making a new Future object when it can, as an
+     optimization. But this is correct.
+
+     core_ can't be moved, it is explicitly disallowed (as is copying). But
+     if there's ever a reason to allow it, this is one place that makes that
+     assumption and would need to be fixed. We use a standard shared pointer
+     for core_ (by copying it in), which means in essence obj holds a shared
+     pointer to itself.  But this shouldn't leak because Promise will not
+     outlive the continuation, because Promise will setException() with a
+     broken Promise if it is destructed before completed. We could use a
+     weak pointer but it would have to be converted to a shared pointer when
+     func is executed (because the Future returned by func may possibly
+     persist beyond the callback, if it gets moved), and so it is an
+     optimization to just make it shared from the get-go.
+
+     We have to move in the Promise and func using the MoveWrapper
+     hack. (func could be copied but it's a big drag on perf).
+
+     Two subtle but important points about this design. detail::Core has no
+     back pointers to Future or Promise, so if Future or Promise get moved
+     (and they will be moved in performant code) we don't have to do
+     anything fancy. And because we store the continuation in the
+     detail::Core, not in the Future, we can execute the continuation even
+     after the Future has gone out of scope. This is an intentional design
+     decision. It is likely we will want to be able to cancel a continuation
+     in some circumstances, but I think it should be explicit not implicit
+     in the destruction of the Future used to create it.
+     */
+  setCallback_(
+    [p, funcm](Try<T>&& t) mutable {
+      p->fulfil([&]() {
+          return (*funcm)(std::move(t));
+        });
+    });
+
+  return std::move(f);
+}
+
+// Variant: f.then([](T&& t){ return t; });
+template <class T>
+template <class F>
+typename std::enable_if<
+  !std::is_same<T, void>::value &&
+  !isFuture<typename std::result_of<
+    F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
+  Future<typename std::result_of<
+    F(typename detail::AliasIfVoid<T>::type&&)>::type> >::type
+Future<T>::then(F&& func) {
+  typedef typename std::result_of<F(T&&)>::type B;
+
+  throwIfInvalid();
+
+  folly::MoveWrapper<Promise<B>> p;
+  folly::MoveWrapper<F> funcm(std::forward<F>(func));
+  auto f = p->getFuture();
+
+  setCallback_(
+    [p, funcm](Try<T>&& t) mutable {
+      if (t.hasException()) {
+        p->setException(t.getException());
+      } else {
+        p->fulfil([&]() {
+          return (*funcm)(std::move(t.value()));
+        });
+      }
+    });
+
+  return std::move(f);
+}
+
+// Variant: f.then([](){ return; });
+template <class T>
+template <class F>
+typename std::enable_if<
+  std::is_same<T, void>::value &&
+  !isFuture<typename std::result_of<F()>::type>::value,
+  Future<typename std::result_of<F()>::type> >::type
+Future<T>::then(F&& func) {
+  typedef typename std::result_of<F()>::type B;
+
+  throwIfInvalid();
+
+  folly::MoveWrapper<Promise<B>> p;
+  folly::MoveWrapper<F> funcm(std::forward<F>(func));
+  auto f = p->getFuture();
+
+  setCallback_(
+    [p, funcm](Try<T>&& t) mutable {
+      if (t.hasException()) {
+        p->setException(t.getException());
+      } else {
+        p->fulfil([&]() {
+          return (*funcm)();
+        });
+      }
+    });
+
+  return std::move(f);
+}
+
+// Variant: f.then([](Try<T>&& t){ return makeFuture<T>(t.value()); });
+template <class T>
+template <class F>
+typename std::enable_if<
+  isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
+  Future<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
+Future<T>::then(F&& func) {
+  typedef typename std::result_of<F(Try<T>&&)>::type::value_type B;
+
+  throwIfInvalid();
+
+  // wrap these so we can move them into the lambda
+  folly::MoveWrapper<Promise<B>> p;
+  folly::MoveWrapper<F> funcm(std::forward<F>(func));
+
+  // grab the Future now before we lose our handle on the Promise
+  auto f = p->getFuture();
+
+  setCallback_(
+    [p, funcm](Try<T>&& t) mutable {
+      try {
+        auto f2 = (*funcm)(std::move(t));
+        // that didn't throw, now we can steal p
+        f2.setCallback_([p](Try<B>&& b) mutable {
+            p->fulfilTry(std::move(b));
+          });
+      } catch (...) {
+        p->setException(std::current_exception());
+      }
+    });
+
+  return std::move(f);
+}
+
+// Variant: f.then([](T&& t){ return makeFuture<T>(t); });
+template <class T>
+template <class F>
+typename std::enable_if<
+  !std::is_same<T, void>::value &&
+  isFuture<typename std::result_of<
+    F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
+  Future<typename std::result_of<
+    F(typename detail::AliasIfVoid<T>::type&&)>::type::value_type> >::type
+Future<T>::then(F&& func) {
+  typedef typename std::result_of<F(T&&)>::type::value_type B;
+
+  throwIfInvalid();
+
+  folly::MoveWrapper<Promise<B>> p;
+  folly::MoveWrapper<F> funcm(std::forward<F>(func));
+  auto f = p->getFuture();
+
+  setCallback_(
+    [p, funcm](Try<T>&& t) mutable {
+      if (t.hasException()) {
+        p->setException(t.getException());
+      } else {
+        try {
+          auto f2 = (*funcm)(std::move(t.value()));
+          f2.setCallback_([p](Try<B>&& b) mutable {
+              p->fulfilTry(std::move(b));
+            });
+        } catch (...) {
+          p->setException(std::current_exception());
+        }
+      }
+    });
+
+  return std::move(f);
+}
+
+// Variant: f.then([](){ return makeFuture(); });
+template <class T>
+template <class F>
+typename std::enable_if<
+  std::is_same<T, void>::value &&
+  isFuture<typename std::result_of<F()>::type>::value,
+  Future<typename std::result_of<F()>::type::value_type> >::type
+Future<T>::then(F&& func) {
+  typedef typename std::result_of<F()>::type::value_type B;
+
+  throwIfInvalid();
+
+  folly::MoveWrapper<Promise<B>> p;
+  folly::MoveWrapper<F> funcm(std::forward<F>(func));
+
+  auto f = p->getFuture();
+
+  setCallback_(
+    [p, funcm](Try<T>&& t) mutable {
+      if (t.hasException()) {
+        p->setException(t.getException());
+      } else {
+        try {
+          auto f2 = (*funcm)();
+          f2.setCallback_([p](Try<B>&& b) mutable {
+              p->fulfilTry(std::move(b));
+            });
+        } catch (...) {
+          p->setException(std::current_exception());
+        }
+      }
+    });
+
+  return std::move(f);
+}
+
+template <class T>
+Future<void> Future<T>::then() {
+  return then([] (Try<T>&& t) {});
+}
+
+template <class T>
+typename std::add_lvalue_reference<T>::type Future<T>::value() {
+  throwIfInvalid();
+
+  return core_->getTry().value();
+}
+
+template <class T>
+typename std::add_lvalue_reference<const T>::type Future<T>::value() const {
+  throwIfInvalid();
+
+  return core_->getTry().value();
+}
+
+template <class T>
+Try<T>& Future<T>::getTry() {
+  throwIfInvalid();
+
+  return core_->getTry();
+}
+
+template <class T>
+template <typename Executor>
+inline Future<T> Future<T>::via(Executor* executor) && {
+  throwIfInvalid();
+
+  this->deactivate();
+  core_->setExecutor(executor);
+
+  return std::move(*this);
+}
+
+template <class T>
+template <typename Executor>
+inline Future<T> Future<T>::via(Executor* executor) & {
+  throwIfInvalid();
+
+  MoveWrapper<Promise<T>> p;
+  auto f = p->getFuture();
+  then([p](Try<T>&& t) mutable { p->fulfilTry(std::move(t)); });
+  return std::move(f).via(executor);
+}
+
+template <class T>
+bool Future<T>::isReady() const {
+  throwIfInvalid();
+  return core_->ready();
+}
+
+template <class T>
+void Future<T>::raise(std::exception_ptr exception) {
+  core_->raise(exception);
+}
+
+// makeFuture
+
+template <class T>
+Future<typename std::decay<T>::type> makeFuture(T&& t) {
+  Promise<typename std::decay<T>::type> p;
+  auto f = p.getFuture();
+  p.setValue(std::forward<T>(t));
+  return std::move(f);
+}
+
+inline // for multiple translation units
+Future<void> makeFuture() {
+  Promise<void> p;
+  auto f = p.getFuture();
+  p.setValue();
+  return std::move(f);
+}
+
+template <class F>
+auto makeFutureTry(
+    F&& func,
+    typename std::enable_if<!std::is_reference<F>::value, bool>::type sdf)
+    -> Future<decltype(func())> {
+  Promise<decltype(func())> p;
+  auto f = p.getFuture();
+  p.fulfil(
+    [&func]() {
+      return (func)();
+    });
+  return std::move(f);
+}
+
+template <class F>
+auto makeFutureTry(F const& func) -> Future<decltype(func())> {
+  F copy = func;
+  return makeFutureTry(std::move(copy));
+}
+
+template <class T>
+Future<T> makeFuture(std::exception_ptr const& e) {
+  Promise<T> p;
+  auto f = p.getFuture();
+  p.setException(e);
+  return std::move(f);
+}
+
+template <class T, class E>
+typename std::enable_if<std::is_base_of<std::exception, E>::value,
+                        Future<T>>::type
+makeFuture(E const& e) {
+  Promise<T> p;
+  auto f = p.getFuture();
+  p.fulfil([&]() -> T { throw e; });
+  return std::move(f);
+}
+
+template <class T>
+Future<T> makeFuture(Try<T>&& t) {
+  try {
+    return makeFuture<T>(std::move(t.value()));
+  } catch (...) {
+    return makeFuture<T>(std::current_exception());
+  }
+}
+
+template <>
+inline Future<void> makeFuture(Try<void>&& t) {
+  try {
+    t.throwIfFailed();
+    return makeFuture();
+  } catch (...) {
+    return makeFuture<void>(std::current_exception());
+  }
+}
+
+// via
+template <typename Executor>
+Future<void> via(Executor* executor) {
+  return makeFuture().via(executor);
+}
+
+// when (variadic)
+
+template <typename... Fs>
+typename detail::VariadicContext<
+  typename std::decay<Fs>::type::value_type...>::type
+whenAll(Fs&&... fs)
+{
+  auto ctx =
+    new detail::VariadicContext<typename std::decay<Fs>::type::value_type...>();
+  ctx->total = sizeof...(fs);
+  auto f_saved = ctx->p.getFuture();
+  detail::whenAllVariadicHelper(ctx,
+    std::forward<typename std::decay<Fs>::type>(fs)...);
+  return std::move(f_saved);
+}
+
+// when (iterator)
+
+template <class InputIterator>
+Future<
+  std::vector<
+  Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
+whenAll(InputIterator first, InputIterator last)
+{
+  typedef
+    typename std::iterator_traits<InputIterator>::value_type::value_type T;
+
+  auto n = std::distance(first, last);
+  if (n == 0) {
+    return makeFuture(std::vector<Try<T>>());
+  }
+
+  auto ctx = new detail::WhenAllContext<T>();
+
+  ctx->results.resize(n);
+
+  auto f_saved = ctx->p.getFuture();
+
+  for (size_t i = 0; first != last; ++first, ++i) {
+     assert(i < n);
+     auto& f = *first;
+     f.setCallback_([ctx, i, n](Try<T>&& t) {
+         ctx->results[i] = std::move(t);
+         if (++ctx->count == n) {
+           ctx->p.setValue(std::move(ctx->results));
+           delete ctx;
+         }
+       });
+  }
+
+  return std::move(f_saved);
+}
+
+template <class InputIterator>
+Future<
+  std::pair<size_t,
+            Try<
+              typename
+              std::iterator_traits<InputIterator>::value_type::value_type> > >
+whenAny(InputIterator first, InputIterator last) {
+  typedef
+    typename std::iterator_traits<InputIterator>::value_type::value_type T;
+
+  auto ctx = new detail::WhenAnyContext<T>(std::distance(first, last));
+  auto f_saved = ctx->p.getFuture();
+
+  for (size_t i = 0; first != last; first++, i++) {
+    auto& f = *first;
+    f.setCallback_([i, ctx](Try<T>&& t) {
+      if (!ctx->done.exchange(true)) {
+        ctx->p.setValue(std::make_pair(i, std::move(t)));
+      }
+      ctx->decref();
+    });
+  }
+
+  return std::move(f_saved);
+}
+
+template <class InputIterator>
+Future<std::vector<std::pair<size_t, Try<typename
+  std::iterator_traits<InputIterator>::value_type::value_type>>>>
+whenN(InputIterator first, InputIterator last, size_t n) {
+  typedef typename
+    std::iterator_traits<InputIterator>::value_type::value_type T;
+  typedef std::vector<std::pair<size_t, Try<T>>> V;
+
+  struct ctx_t {
+    V v;
+    size_t completed;
+    Promise<V> p;
+  };
+  auto ctx = std::make_shared<ctx_t>();
+  ctx->completed = 0;
+
+  // for each completed Future, increase count and add to vector, until we
+  // have n completed futures at which point we fulfil our Promise with the
+  // vector
+  auto it = first;
+  size_t i = 0;
+  while (it != last) {
+    it->then([ctx, n, i](Try<T>&& t) {
+      auto& v = ctx->v;
+      auto c = ++ctx->completed;
+      if (c <= n) {
+        assert(ctx->v.size() < n);
+        v.push_back(std::make_pair(i, std::move(t)));
+        if (c == n) {
+          ctx->p.fulfilTry(Try<V>(std::move(v)));
+        }
+      }
+    });
+
+    it++;
+    i++;
+  }
+
+  if (i < n) {
+    ctx->p.setException(std::runtime_error("Not enough futures"));
+  }
+
+  return ctx->p.getFuture();
+}
+
+template <typename T>
+Future<T>
+waitWithSemaphore(Future<T>&& f) {
+  Baton<> baton;
+  auto done = f.then([&](Try<T> &&t) {
+    baton.post();
+    return std::move(t.value());
+  });
+  baton.wait();
+  while (!done.isReady()) {
+    // There's a race here between the return here and the actual finishing of
+    // the future. f is completed, but the setup may not have finished on done
+    // after the baton has posted.
+    std::this_thread::yield();
+  }
+  return done;
+}
+
+template<>
+inline Future<void> waitWithSemaphore<void>(Future<void>&& f) {
+  Baton<> baton;
+  auto done = f.then([&](Try<void> &&t) {
+    baton.post();
+    t.value();
+  });
+  baton.wait();
+  while (!done.isReady()) {
+    // There's a race here between the return here and the actual finishing of
+    // the future. f is completed, but the setup may not have finished on done
+    // after the baton has posted.
+    std::this_thread::yield();
+  }
+  return done;
+}
+
+template <typename T, class Duration>
+Future<T>
+waitWithSemaphore(Future<T>&& f, Duration timeout) {
+  auto baton = std::make_shared<Baton<>>();
+  auto done = f.then([baton](Try<T> &&t) {
+    baton->post();
+    return std::move(t.value());
+  });
+  baton->timed_wait(std::chrono::system_clock::now() + timeout);
+  return done;
+}
+
+template <class Duration>
+Future<void>
+waitWithSemaphore(Future<void>&& f, Duration timeout) {
+  auto baton = std::make_shared<Baton<>>();
+  auto done = f.then([baton](Try<void> &&t) {
+    baton->post();
+    t.value();
+  });
+  baton->timed_wait(std::chrono::system_clock::now() + timeout);
+  return done;
+}
+
+}}
+
+// I haven't included a Future<T&> specialization because I don't forsee us
+// using it, however it is not difficult to add when needed. Refer to
+// Future<void> for guidance. std::future and boost::future code would also be
+// instructive.
diff --git a/folly/wangle/futures/Future.h b/folly/wangle/futures/Future.h
new file mode 100644 (file)
index 0000000..cdea93d
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <exception>
+#include <functional>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+#include <folly/MoveWrapper.h>
+#include <folly/wangle/futures/Promise.h>
+#include <folly/wangle/futures/Try.h>
+
+namespace folly { namespace wangle {
+
+namespace detail {
+  template <class> struct Core;
+  template <class...> struct VariadicContext;
+
+  template <class T>
+  struct AliasIfVoid {
+    typedef typename std::conditional<
+      std::is_same<T, void>::value,
+      int,
+      T>::type type;
+  };
+}
+
+template <class> struct Promise;
+
+template <typename T> struct isFuture;
+
+template <class T>
+class Future {
+ public:
+  typedef T value_type;
+
+  // not copyable
+  Future(Future const&) = delete;
+  Future& operator=(Future const&) = delete;
+
+  // movable
+  Future(Future&&) noexcept;
+  Future& operator=(Future&&);
+
+  ~Future();
+
+  /** Return the reference to result. Should not be called if !isReady().
+    Will rethrow the exception if an exception has been
+    captured.
+
+    This function is not thread safe - the returned Future can only
+    be executed from the thread that the executor runs it in.
+    See below for a thread safe version
+    */
+  typename std::add_lvalue_reference<T>::type
+  value();
+  typename std::add_lvalue_reference<const T>::type
+  value() const;
+
+  /// Returns an inactive Future which will call back on the other side of
+  /// executor (when it is activated).
+  ///
+  /// NB remember that Futures activate when they destruct. This is good,
+  /// it means that this will work:
+  ///
+  ///   f.via(e).then(a).then(b);
+  ///
+  /// a and b will execute in the same context (the far side of e), because
+  /// the Future (temporary variable) created by via(e) does not call back
+  /// until it destructs, which is after then(a) and then(b) have been wired
+  /// up.
+  ///
+  /// But this is still racy:
+  ///
+  ///   f = f.via(e).then(a);
+  ///   f.then(b);
+  // The ref-qualifier allows for `this` to be moved out so we
+  // don't get access-after-free situations in chaining.
+  // https://akrzemi1.wordpress.com/2014/06/02/ref-qualifiers/
+  template <typename Executor>
+  Future<T> via(Executor* executor) &&;
+
+  /// This variant creates a new future, where the ref-qualifier && version
+  /// moves `this` out. This one is less efficient but avoids confusing users
+  /// when "return f.via(x);" fails.
+  template <typename Executor>
+  Future<T> via(Executor* executor) &;
+
+  /** True when the result (or exception) is ready. */
+  bool isReady() const;
+
+  /** A reference to the Try of the value */
+  Try<T>& getTry();
+
+  /** When this Future has completed, execute func which is a function that
+    takes a Try<T>&&. A Future for the return type of func is
+    returned. e.g.
+
+    Future<string> f2 = f1.then([](Try<T>&&) { return string("foo"); });
+
+    The Future given to the functor is ready, and the functor may call
+    value(), which may rethrow if this has captured an exception. If func
+    throws, the exception will be captured in the Future that is returned.
+    */
+  /* TODO n3428 and other async frameworks have something like then(scheduler,
+     Future), we might want to support a similar API which could be
+     implemented a little more efficiently than
+     f.via(executor).then(callback) */
+  template <class F>
+  typename std::enable_if<
+    !isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
+    Future<typename std::result_of<F(Try<T>&&)>::type> >::type
+  then(F&& func);
+
+  /// Variant where func takes a T directly, bypassing a try. Any exceptions
+  /// will be implicitly passed on to the resultant Future.
+  ///
+  ///   Future<int> f = makeFuture<int>(42).then([](int i) { return i+1; });
+  template <class F>
+  typename std::enable_if<
+    !std::is_same<T, void>::value &&
+    !isFuture<typename std::result_of<
+      F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
+    Future<typename std::result_of<
+      F(typename detail::AliasIfVoid<T>::type&&)>::type> >::type
+  then(F&& func);
+
+  /// Like the above variant, but for void futures. That is, func takes no
+  /// argument.
+  ///
+  ///   Future<int> f = makeFuture().then([] { return 42; });
+  template <class F>
+  typename std::enable_if<
+    std::is_same<T, void>::value &&
+    !isFuture<typename std::result_of<F()>::type>::value,
+    Future<typename std::result_of<F()>::type> >::type
+  then(F&& func);
+
+  /// Variant where func returns a Future<T> instead of a T. e.g.
+  ///
+  ///   Future<string> f2 = f1.then(
+  ///     [](Try<T>&&) { return makeFuture<string>("foo"); });
+  template <class F>
+  typename std::enable_if<
+    isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
+    Future<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
+  then(F&& func);
+
+  /// Variant where func returns a Future<T2> and takes a T directly, bypassing
+  /// a Try. Any exceptions will be implicitly passed on to the resultant
+  /// Future. For example,
+  ///
+  ///   Future<int> f = makeFuture<int>(42).then(
+  ///     [](int i) { return makeFuture<int>(i+1); });
+  template <class F>
+  typename std::enable_if<
+    !std::is_same<T, void>::value &&
+    isFuture<typename std::result_of<
+      F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
+    Future<typename std::result_of<
+      F(typename detail::AliasIfVoid<T>::type&&)>::type::value_type> >::type
+  then(F&& func);
+
+  /// Like the above variant, but for void futures. That is, func takes no
+  /// argument and returns a future.
+  ///
+  ///   Future<int> f = makeFuture().then(
+  ///     [] { return makeFuture<int>(42); });
+  template <class F>
+  typename std::enable_if<
+    std::is_same<T, void>::value &&
+    isFuture<typename std::result_of<F()>::type>::value,
+    Future<typename std::result_of<F()>::type::value_type> >::type
+  then(F&& func);
+
+  /// Variant where func is an ordinary function (static method, method)
+  ///
+  ///   R doWork(Try<T>&&);
+  ///
+  ///   Future<R> f2 = f1.then(doWork);
+  ///
+  /// or
+  ///
+  ///   struct Worker {
+  ///     static R doWork(Try<T>&&); }
+  ///
+  ///   Future<R> f2 = f1.then(&Worker::doWork);
+  template <class = T, class R = std::nullptr_t>
+  typename std::enable_if<!isFuture<R>::value, Future<R>>::type
+  inline then(R(*func)(Try<T>&&)) {
+    return then([func](Try<T>&& t) {
+      return (*func)(std::move(t));
+    });
+  }
+
+  /// Variant where func returns a Future<R> instead of a R. e.g.
+  ///
+  ///   struct Worker {
+  ///     Future<R> doWork(Try<T>&&); }
+  ///
+  ///   Future<R> f2 = f1.then(&Worker::doWork);
+  template <class = T, class R = std::nullptr_t>
+  typename std::enable_if<isFuture<R>::value, R>::type
+  inline then(R(*func)(Try<T>&&)) {
+    return then([func](Try<T>&& t) {
+      return (*func)(std::move(t));
+    });
+  }
+
+  /// Variant where func is an member function
+  ///
+  ///   struct Worker {
+  ///     R doWork(Try<T>&&); }
+  ///
+  ///   Worker *w;
+  ///   Future<R> f2 = f1.then(w, &Worker::doWork);
+  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
+  typename std::enable_if<!isFuture<R>::value, Future<R>>::type
+  inline then(Caller *instance, R(Caller::*func)(Try<T>&&)) {
+    return then([instance, func](Try<T>&& t) {
+      return (instance->*func)(std::move(t));
+    });
+  }
+
+  // Same as above, but func takes void instead of Try<void>&&
+  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
+  typename std::enable_if<
+      std::is_same<T, void>::value && !isFuture<R>::value, Future<R>>::type
+  inline then(Caller *instance, R(Caller::*func)()) {
+    return then([instance, func]() {
+      return (instance->*func)();
+    });
+  }
+
+  // Same as above, but func takes T&& instead of Try<T>&&
+  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
+  typename std::enable_if<
+      !std::is_same<T, void>::value && !isFuture<R>::value, Future<R>>::type
+  inline then(
+      Caller *instance,
+      R(Caller::*func)(typename detail::AliasIfVoid<T>::type&&)) {
+    return then([instance, func](T&& t) {
+      return (instance->*func)(std::move(t));
+    });
+  }
+
+  /// Variant where func returns a Future<R> instead of a R. e.g.
+  ///
+  ///   struct Worker {
+  ///     Future<R> doWork(Try<T>&&); }
+  ///
+  ///   Worker *w;
+  ///   Future<R> f2 = f1.then(w, &Worker::doWork);
+  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
+  typename std::enable_if<isFuture<R>::value, R>::type
+  inline then(Caller *instance, R(Caller::*func)(Try<T>&&)) {
+    return then([instance, func](Try<T>&& t) {
+      return (instance->*func)(std::move(t));
+    });
+  }
+
+  // Same as above, but func takes void instead of Try<void>&&
+  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
+  typename std::enable_if<
+      std::is_same<T, void>::value && isFuture<R>::value, R>::type
+  inline then(Caller *instance, R(Caller::*func)()) {
+    return then([instance, func]() {
+      return (instance->*func)();
+    });
+  }
+
+  // Same as above, but func takes T&& instead of Try<T>&&
+  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
+  typename std::enable_if<
+      !std::is_same<T, void>::value && isFuture<R>::value, R>::type
+  inline then(
+      Caller *instance,
+      R(Caller::*func)(typename detail::AliasIfVoid<T>::type&&)) {
+    return then([instance, func](T&& t) {
+      return (instance->*func)(std::move(t));
+    });
+  }
+
+  /// Convenience method for ignoring the value and creating a Future<void>.
+  /// Exceptions still propagate.
+  Future<void> then();
+
+  /// This is not the method you're looking for.
+  ///
+  /// This needs to be public because it's used by make* and when*, and it's
+  /// not worth listing all those and their fancy template signatures as
+  /// friends. But it's not for public consumption.
+  template <class F>
+  void setCallback_(F&& func);
+
+  /// A Future's callback is executed when all three of these conditions have
+  /// become true: it has a value (set by the Promise), it has a callback (set
+  /// by then), and it is active (active by default).
+  ///
+  /// Inactive Futures will activate upon destruction.
+  Future<T>& activate() & {
+    core_->activate();
+    return *this;
+  }
+  Future<T>& deactivate() & {
+    core_->deactivate();
+    return *this;
+  }
+  Future<T> activate() && {
+    core_->activate();
+    return std::move(*this);
+  }
+  Future<T> deactivate() && {
+    core_->deactivate();
+    return std::move(*this);
+  }
+
+  bool isActive() {
+    return core_->isActive();
+  }
+
+  template <class E>
+  void raise(E&& exception) {
+    raise(std::make_exception_ptr(std::forward<E>(exception)));
+  }
+
+  /// Raise an interrupt. If the promise holder has an interrupt
+  /// handler it will be called and potentially stop asynchronous work from
+  /// being done. This is advisory only - a promise holder may not set an
+  /// interrupt handler, or may do anything including ignore. But, if you know
+  /// your future supports this the most likely result is stopping or
+  /// preventing the asynchronous operation (if in time), and the promise
+  /// holder setting an exception on the future. (That may happen
+  /// asynchronously, of course.)
+  void raise(std::exception_ptr interrupt);
+
+  void cancel() {
+    raise(FutureCancellation());
+  }
+
+ private:
+  typedef detail::Core<T>* corePtr;
+
+  // shared core state object
+  corePtr core_;
+
+  explicit
+  Future(corePtr obj) : core_(obj) {}
+
+  void detach();
+
+  void throwIfInvalid() const;
+
+  friend class Promise<T>;
+};
+
+/**
+  Make a completed Future by moving in a value. e.g.
+
+    string foo = "foo";
+    auto f = makeFuture(std::move(foo));
+
+  or
+
+    auto f = makeFuture<string>("foo");
+*/
+template <class T>
+Future<typename std::decay<T>::type> makeFuture(T&& t);
+
+/** Make a completed void Future. */
+Future<void> makeFuture();
+
+/** Make a completed Future by executing a function. If the function throws
+  we capture the exception, otherwise we capture the result. */
+template <class F>
+auto makeFutureTry(
+  F&& func,
+  typename std::enable_if<
+    !std::is_reference<F>::value, bool>::type sdf = false)
+  -> Future<decltype(func())>;
+
+template <class F>
+auto makeFutureTry(
+  F const& func)
+  -> Future<decltype(func())>;
+
+/// Make a failed Future from an exception_ptr.
+/// Because the Future's type cannot be inferred you have to specify it, e.g.
+///
+///   auto f = makeFuture<string>(std::current_exception());
+template <class T>
+Future<T> makeFuture(std::exception_ptr const& e);
+
+/** Make a Future from an exception type E that can be passed to
+  std::make_exception_ptr(). */
+template <class T, class E>
+typename std::enable_if<std::is_base_of<std::exception, E>::value,
+                        Future<T>>::type
+makeFuture(E const& e);
+
+/** Make a Future out of a Try */
+template <class T>
+Future<T> makeFuture(Try<T>&& t);
+
+/*
+ * Return a new Future that will call back on the given Executor.
+ * This is just syntactic sugar for makeFuture().via(executor)
+ *
+ * @param executor the Executor to call back on
+ *
+ * @returns a void Future that will call back on the given executor
+ */
+template <typename Executor>
+Future<void> via(Executor* executor);
+
+/** When all the input Futures complete, the returned Future will complete.
+  Errors do not cause early termination; this Future will always succeed
+  after all its Futures have finished (whether successfully or with an
+  error).
+
+  The Futures are moved in, so your copies are invalid. If you need to
+  chain further from these Futures, use the variant with an output iterator.
+
+  XXX is this still true?
+  This function is thread-safe for Futures running on different threads.
+
+  The return type for Future<T> input is a Future<std::vector<Try<T>>>
+  */
+template <class InputIterator>
+Future<std::vector<Try<
+  typename std::iterator_traits<InputIterator>::value_type::value_type>>>
+whenAll(InputIterator first, InputIterator last);
+
+/// This version takes a varying number of Futures instead of an iterator.
+/// The return type for (Future<T1>, Future<T2>, ...) input
+/// is a Future<std::tuple<Try<T1>, Try<T2>, ...>>.
+/// The Futures are moved in, so your copies are invalid.
+template <typename... Fs>
+typename detail::VariadicContext<
+  typename std::decay<Fs>::type::value_type...>::type
+whenAll(Fs&&... fs);
+
+/** The result is a pair of the index of the first Future to complete and
+  the Try. If multiple Futures complete at the same time (or are already
+  complete when passed in), the "winner" is chosen non-deterministically.
+
+  This function is thread-safe for Futures running on different threads.
+  */
+template <class InputIterator>
+Future<std::pair<
+  size_t,
+  Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
+whenAny(InputIterator first, InputIterator last);
+
+/** when n Futures have completed, the Future completes with a vector of
+  the index and Try of those n Futures (the indices refer to the original
+  order, but the result vector will be in an arbitrary order)
+
+  Not thread safe.
+  */
+template <class InputIterator>
+Future<std::vector<std::pair<
+  size_t,
+  Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>
+whenN(InputIterator first, InputIterator last, size_t n);
+
+/** Wait for the given future to complete on a semaphore. Returns a completed
+ * future containing the result.
+ *
+ * NB if the promise for the future would be fulfilled in the same thread that
+ * you call this, it will deadlock.
+ */
+template <class T>
+Future<T> waitWithSemaphore(Future<T>&& f);
+
+/** Wait for up to `timeout` for the given future to complete. Returns a future
+ * which may or may not be completed depending whether the given future
+ * completed in time
+ *
+ * Note: each call to this starts a (short-lived) thread and allocates memory.
+ */
+template <typename T, class Duration>
+Future<T> waitWithSemaphore(Future<T>&& f, Duration timeout);
+
+}} // folly::wangle
+
+#include <folly/wangle/futures/Future-inl.h>
diff --git a/folly/wangle/futures/InlineExecutor.cpp b/folly/wangle/futures/InlineExecutor.cpp
new file mode 100644 (file)
index 0000000..1ef2f06
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
diff --git a/folly/wangle/futures/InlineExecutor.h b/folly/wangle/futures/InlineExecutor.h
new file mode 100644 (file)
index 0000000..e692408
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <folly/Executor.h>
+
+namespace folly { namespace wangle {
+
+  /// When work is "queued", execute it immediately inline.
+  /// Usually when you think you want this, you actually want a
+  /// QueuedImmediateExecutor.
+  class InlineExecutor : public Executor {
+   public:
+    void add(Func f) override {
+      f();
+    }
+  };
+
+}}
diff --git a/folly/wangle/futures/ManualExecutor.cpp b/folly/wangle/futures/ManualExecutor.cpp
new file mode 100644 (file)
index 0000000..9b68b1a
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/wangle/futures/ManualExecutor.h>
+
+#include <string.h>
+#include <string>
+#include <tuple>
+
+#include <stdexcept>
+
+namespace folly { namespace wangle {
+
+ManualExecutor::ManualExecutor() {
+  if (sem_init(&sem_, 0, 0) == -1) {
+    throw std::runtime_error(std::string("sem_init: ") + strerror(errno));
+  }
+}
+
+void ManualExecutor::add(Func callback) {
+  std::lock_guard<std::mutex> lock(lock_);
+  funcs_.push(std::move(callback));
+  sem_post(&sem_);
+}
+
+size_t ManualExecutor::run() {
+  size_t count;
+  size_t n;
+  Func func;
+
+  {
+    std::lock_guard<std::mutex> lock(lock_);
+
+    while (!scheduledFuncs_.empty()) {
+      auto& sf = scheduledFuncs_.top();
+      if (sf.time > now_)
+        break;
+      funcs_.push(sf.func);
+      scheduledFuncs_.pop();
+    }
+
+    n = funcs_.size();
+  }
+
+  for (count = 0; count < n; count++) {
+    {
+      std::lock_guard<std::mutex> lock(lock_);
+      if (funcs_.empty()) {
+        break;
+      }
+
+      // Balance the semaphore so it doesn't grow without bound
+      // if nobody is calling wait().
+      // This may fail (with EAGAIN), that's fine.
+      sem_trywait(&sem_);
+
+      func = std::move(funcs_.front());
+      funcs_.pop();
+    }
+    func();
+  }
+
+  return count;
+}
+
+void ManualExecutor::wait() {
+  while (true) {
+    {
+      std::lock_guard<std::mutex> lock(lock_);
+      if (!funcs_.empty())
+        break;
+    }
+
+    auto ret = sem_wait(&sem_);
+    if (ret == 0) {
+      break;
+    }
+    if (errno != EINVAL) {
+      throw std::runtime_error(std::string("sem_wait: ") + strerror(errno));
+    }
+  }
+}
+
+void ManualExecutor::advanceTo(TimePoint const& t) {
+  if (t > now_) {
+    now_ = t;
+  }
+  run();
+}
+
+}} // namespace
diff --git a/folly/wangle/futures/ManualExecutor.h b/folly/wangle/futures/ManualExecutor.h
new file mode 100644 (file)
index 0000000..72a1c1d
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <folly/wangle/futures/ScheduledExecutor.h>
+#include <semaphore.h>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <cstdio>
+
+namespace folly { namespace wangle {
+  /// A ManualExecutor only does work when you turn the crank, by calling
+  /// run() or indirectly with makeProgress() or waitFor().
+  ///
+  /// The clock for a manual executor starts at 0 and advances only when you
+  /// ask it to. i.e. time is also under manual control.
+  ///
+  /// NB No attempt has been made to make anything other than add and schedule
+  /// threadsafe.
+  class ManualExecutor : public ScheduledExecutor {
+   public:
+    ManualExecutor();
+
+    void add(Func) override;
+
+    /// Do work. Returns the number of functions that were executed (maybe 0).
+    /// Non-blocking, in the sense that we don't wait for work (we can't
+    /// control whether one of the functions blocks).
+    /// This is stable, it will not chase an ever-increasing tail of work.
+    /// This also means, there may be more work available to perform at the
+    /// moment that this returns.
+    size_t run();
+
+    /// Wait for work to do.
+    void wait();
+
+    /// Wait for work to do, and do it.
+    void makeProgress() {
+      wait();
+      run();
+    }
+
+    /// makeProgress until this Future is ready.
+    template <class F> void waitFor(F const& f) {
+      // TODO(5427828)
+#if 0
+      while (!f.isReady())
+        makeProgress();
+#else
+      while (!f.isReady()) {
+        run();
+      }
+#endif
+
+    }
+
+    virtual void scheduleAt(Func&& f, TimePoint const& t) override {
+      std::lock_guard<std::mutex> lock(lock_);
+      scheduledFuncs_.emplace(t, std::move(f));
+      sem_post(&sem_);
+    }
+
+    /// Advance the clock. The clock never advances on its own.
+    /// Advancing the clock causes some work to be done, if work is available
+    /// to do (perhaps newly available because of the advanced clock).
+    /// If dur is <= 0 this is a noop.
+    void advance(Duration const& dur) {
+      advanceTo(now_ + dur);
+    }
+
+    /// Advance the clock to this absolute time. If t is <= now(),
+    /// this is a noop.
+    void advanceTo(TimePoint const& t);
+
+    TimePoint now() override { return now_; }
+
+   private:
+    std::mutex lock_;
+    std::queue<Func> funcs_;
+    sem_t sem_;
+
+    // helper class to enable ordering of scheduled events in the priority
+    // queue
+    struct ScheduledFunc {
+      TimePoint time;
+      size_t ordinal;
+      Func func;
+
+      ScheduledFunc(TimePoint const& t, Func&& f)
+        : time(t), func(std::move(f))
+      {
+        static size_t seq = 0;
+        ordinal = seq++;
+      }
+
+      bool operator<(ScheduledFunc const& b) const {
+        if (time == b.time)
+          return ordinal < b.ordinal;
+        return time < b.time;
+      }
+    };
+    std::priority_queue<ScheduledFunc> scheduledFuncs_;
+    TimePoint now_ = now_.min();
+  };
+
+}}
diff --git a/folly/wangle/futures/OpaqueCallbackShunt.h b/folly/wangle/futures/OpaqueCallbackShunt.h
new file mode 100644 (file)
index 0000000..2e7dbd1
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/wangle/futures/Promise.h>
+
+namespace folly { namespace wangle {
+
+/// These classes help you wrap an existing C style callback function
+/// into a Future.
+///
+///   void legacy_send_async(..., void (*cb)(void*), void*);
+///
+///   Future<T> wrappedSendAsync(T&& obj) {
+///     auto handle = new OpaqueCallbackShunt<T>(obj);
+///     auto future = handle->promise_.getFuture();
+///     legacy_send_async(..., OpaqueCallbackShunt<T>::callback, handle)
+///     return future;
+///   }
+///
+/// If the legacy function doesn't conform to void (*cb)(void*), use a lambda:
+///
+///   auto cb = [](t1*, t2*, void* arg) {
+///     OpaqueCallbackShunt<T>::callback(arg);
+///   };
+///   legacy_send_async(..., cb, handle);
+
+template <typename T>
+class OpaqueCallbackShunt {
+public:
+  explicit OpaqueCallbackShunt(T&& obj)
+    : obj_(std::move(obj)) { }
+  static void callback(void* arg) {
+    std::unique_ptr<OpaqueCallbackShunt<T>> handle(
+      static_cast<OpaqueCallbackShunt<T>*>(arg));
+    handle->promise_.setValue(std::move(handle->obj_));
+  }
+  folly::wangle::Promise<T> promise_;
+private:
+  T obj_;
+};
+
+}} // folly::wangle
diff --git a/folly/wangle/futures/Promise-inl.h b/folly/wangle/futures/Promise-inl.h
new file mode 100644 (file)
index 0000000..ec1afe1
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <atomic>
+#include <thread>
+
+#include <folly/wangle/futures/WangleException.h>
+#include <folly/wangle/futures/detail/Core.h>
+
+namespace folly { namespace wangle {
+
+template <class T>
+Promise<T>::Promise() : retrieved_(false), core_(new detail::Core<T>())
+{}
+
+template <class T>
+Promise<T>::Promise(Promise<T>&& other) : core_(nullptr) {
+  *this = std::move(other);
+}
+
+template <class T>
+Promise<T>& Promise<T>::operator=(Promise<T>&& other) {
+  std::swap(core_, other.core_);
+  std::swap(retrieved_, other.retrieved_);
+  return *this;
+}
+
+template <class T>
+void Promise<T>::throwIfFulfilled() {
+  if (!core_)
+    throw NoState();
+  if (core_->ready())
+    throw PromiseAlreadySatisfied();
+}
+
+template <class T>
+void Promise<T>::throwIfRetrieved() {
+  if (retrieved_)
+    throw FutureAlreadyRetrieved();
+}
+
+template <class T>
+Promise<T>::~Promise() {
+  detach();
+}
+
+template <class T>
+void Promise<T>::detach() {
+  if (core_) {
+    if (!retrieved_)
+      core_->detachFuture();
+    core_->detachPromise();
+    core_ = nullptr;
+  }
+}
+
+template <class T>
+Future<T> Promise<T>::getFuture() {
+  throwIfRetrieved();
+  retrieved_ = true;
+  return Future<T>(core_);
+}
+
+template <class T>
+template <class E>
+void Promise<T>::setException(E const& e) {
+  setException(std::make_exception_ptr<E>(e));
+}
+
+template <class T>
+void Promise<T>::setException(std::exception_ptr const& e) {
+  throwIfFulfilled();
+  core_->setResult(Try<T>(e));
+}
+
+template <class T>
+void Promise<T>::setInterruptHandler(
+  std::function<void(std::exception_ptr const&)> fn) {
+  core_->setInterruptHandler(std::move(fn));
+}
+
+template <class T>
+void Promise<T>::fulfilTry(Try<T>&& t) {
+  throwIfFulfilled();
+  core_->setResult(std::move(t));
+}
+
+template <class T>
+template <class M>
+void Promise<T>::setValue(M&& v) {
+  static_assert(!std::is_same<T, void>::value,
+                "Use setValue() instead");
+
+  fulfilTry(Try<T>(std::forward<M>(v)));
+}
+
+template <class T>
+void Promise<T>::setValue() {
+  static_assert(std::is_same<T, void>::value,
+                "Use setValue(value) instead");
+
+  fulfilTry(Try<void>());
+}
+
+template <class T>
+template <class F>
+void Promise<T>::fulfil(F&& func) {
+  throwIfFulfilled();
+  fulfilTry(makeTryFunction(std::forward<F>(func)));
+}
+
+}}
diff --git a/folly/wangle/futures/Promise.h b/folly/wangle/futures/Promise.h
new file mode 100644 (file)
index 0000000..31b76cf
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/wangle/futures/Try.h>
+
+namespace folly { namespace wangle {
+
+// forward declaration
+template <class T> class Future;
+
+template <class T>
+class Promise {
+public:
+  Promise();
+  ~Promise();
+
+  // not copyable
+  Promise(Promise const&) = delete;
+  Promise& operator=(Promise const&) = delete;
+
+  // movable
+  Promise(Promise<T>&&);
+  Promise& operator=(Promise<T>&&);
+
+  /** Return a Future tied to the shared core state. This can be called only
+    once, thereafter Future already retrieved exception will be raised. */
+  Future<T> getFuture();
+
+  /** Fulfil the Promise with an exception_ptr, e.g.
+    try {
+      ...
+    } catch (...) {
+      p.setException(std::current_exception());
+    }
+    */
+  void setException(std::exception_ptr const&);
+
+  /** Fulfil the Promise with an exception type E, which can be passed to
+    std::make_exception_ptr(). Useful for originating exceptions. If you
+    caught an exception the exception_ptr form is more appropriate.
+    */
+  template <class E> void setException(E const&);
+
+  /// Set an interrupt handler to handle interrupts. See the documentation for
+  /// Future::raise(). Your handler can do whatever it wants, but if you
+  /// bother to set one then you probably will want to fulfil the promise with
+  /// an exception (or special value) indicating how the interrupt was
+  /// handled.
+  void setInterruptHandler(std::function<void(std::exception_ptr const&)>);
+
+  /** Fulfil this Promise (only for Promise<void>) */
+  void setValue();
+
+  /** Set the value (use perfect forwarding for both move and copy) */
+  template <class M>
+  void setValue(M&& value);
+
+  void fulfilTry(Try<T>&& t);
+
+  /** Fulfil this Promise with the result of a function that takes no
+    arguments and returns something implicitly convertible to T.
+    Captures exceptions. e.g.
+
+    p.fulfil([] { do something that may throw; return a T; });
+  */
+  template <class F>
+  void fulfil(F&& func);
+
+private:
+  typedef typename Future<T>::corePtr corePtr;
+
+  // Whether the Future has been retrieved (a one-time operation).
+  bool retrieved_;
+
+  // shared core state object
+  corePtr core_;
+
+  void throwIfFulfilled();
+  void throwIfRetrieved();
+  void detach();
+};
+
+}}
+
+#include <folly/wangle/futures/Future.h>
+#include <folly/wangle/futures/Promise-inl.h>
diff --git a/folly/wangle/futures/QueuedImmediateExecutor.cpp b/folly/wangle/futures/QueuedImmediateExecutor.cpp
new file mode 100644 (file)
index 0000000..b82a486
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/wangle/futures/QueuedImmediateExecutor.h>
+#include <folly/ThreadLocal.h>
+#include <queue>
+
+namespace folly { namespace wangle {
+
+void QueuedImmediateExecutor::add(Func callback) {
+  thread_local std::queue<Func> q;
+
+  if (q.empty()) {
+    q.push(std::move(callback));
+    while (!q.empty()) {
+      q.front()();
+      q.pop();
+    }
+  } else {
+    q.push(callback);
+  }
+}
+
+}} // namespace
diff --git a/folly/wangle/futures/QueuedImmediateExecutor.h b/folly/wangle/futures/QueuedImmediateExecutor.h
new file mode 100644 (file)
index 0000000..a82c32d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Executor.h>
+
+namespace folly { namespace wangle {
+
+/**
+ * Runs inline like InlineExecutor, but with a queue so that any tasks added
+ * to this executor by one of its own callbacks will be queued instead of
+ * executed inline (nested). This is usually better behavior than Inline.
+ */
+class QueuedImmediateExecutor : public Executor {
+ public:
+  void add(Func) override;
+};
+
+}} // namespace
diff --git a/folly/wangle/futures/README.md b/folly/wangle/futures/README.md
new file mode 100644 (file)
index 0000000..a23d7c6
--- /dev/null
@@ -0,0 +1,278 @@
+# Wangle
+Wangle is a framework for expressing asynchronous code in C++ using the Future pattern.
+
+**wan•gle** |ˈwaNGgÉ™l| informal  
+*verb*  
+Obtain (something that is desired) by persuading others to comply or by manipulating events.
+
+*noun*  
+A framework for expressing asynchronous control flow in C++, that is composable and easily translated to/from synchronous code.
+
+*synonyms*  
+[Finagle](http://twitter.github.io/finagle/)
+
+Wangle is a futures-based async framework inspired by [Twitter's Finagle](http://twitter.github.io/finagle/) (which is in scala), and (loosely) building upon the existing (but anemic) Futures code found in the C++11 standard ([`std::future`](http://en.cppreference.com/w/cpp/thread/future)) and [`boost::future`](http://www.boost.org/doc/libs/1_53_0/boost/thread/future.hpp) (especially >= 1.53.0). Although inspired by the std::future interface, it is not syntactically drop-in compatible because some ideas didn't translate well enough and we decided to break from the API. But semantically, it should be straightforward to translate from existing std::future code to Wangle.
+
+The primary semantic differences are that Wangle Futures and Promises are not threadsafe; and as does `boost::future`, Wangle supports continuing callbacks (`then()`) and there are helper methods `whenAll()` and `whenAny()` which are important compositional building blocks.
+
+## Brief Synopsis
+
+```C++
+#include <folly/wangle/futures/Future.h>
+using namespace folly::wangle;
+using namespace std;
+
+void foo(int x) {
+  // do something with x
+  cout << "foo(" << x << ")" << endl;
+}
+
+// ...
+
+  cout << "making Promise" << endl;
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  f.then(
+    [](Try<int>&& t) {
+      foo(t.value());
+    });
+  cout << "Future chain made" << endl;
+
+// ... now perhaps in another event callback
+
+  cout << "fulfilling Promise" << endl;
+  p.setValue(42);
+  cout << "Promise fulfilled" << endl;
+```
+
+This would print:
+  
+```
+making Promise
+Future chain made
+fulfilling Promise
+foo(42)
+Promise fulfilled
+```
+
+## User Guide
+
+Let's begin with an example. Consider a simplified Memcache client class with this interface:
+
+```C++
+class MemcacheClient {
+ public:
+  struct GetReply {
+    enum class Result {
+      FOUND,
+      NOT_FOUND,
+      SERVER_ERROR,
+    };
+
+    Result result;
+    // The value when result is FOUND,
+    // The error message when result is SERVER_ERROR or CLIENT_ERROR
+    // undefined otherwise
+    std::string value;
+  };
+
+  GetReply get(std::string key);
+};
+```
+
+This API is synchronous, i.e. when you call `get()` you have to wait for the result. This is very simple, but unfortunately it is also very easy to write very slow code using synchronous APIs.
+
+Now, consider this traditional asynchronous signature for `get()`:
+
+```C++
+int get(std::string key, std::function<void(GetReply)> callback);
+```
+
+When you call `get()`, your asynchronous operation begins and when it finishes your callback will be called with the result. (Unless something goes drastically wrong and you get an error code from `get()`.) Very performant code can be written with an API like this, but for nontrivial applications the code descends into a special kind of spaghetti code affectionately referred to as "callback hell".
+
+The Future-based API looks like this:
+
+```C++
+Future<GetReply> get(std::string key);
+```
+
+A `Future<GetReply>` is a placeholder for the `GetReply` that we will eventually get. A Future usually starts life out "unfulfilled", or incomplete, i.e.:
+
+```C++
+fut.isReady() == false
+fut.value()  // will throw an exception because the Future is not ready
+```
+
+At some point in the future, the Future will have been fulfilled, and we can access its value.
+
+```C++
+fut.isReady() == true
+GetReply& reply = fut.value();
+```
+
+Futures support exceptions. If something exceptional happened, your Future may represent an exception instead of a value. In that case:
+
+```C++
+fut.isReady() == true
+fut.value() // will rethrow the exception
+```
+
+Just what is exceptional depends on the API. In our example we have chosen not to raise exceptions for `SERVER_ERROR`, but represent this explicitly in the `GetReply` object. On the other hand, an astute Memcache veteran would notice that we left `CLIENT_ERROR` out of `GetReply::Result`, and perhaps a `CLIENT_ERROR` would have been raised as an exception, because `CLIENT_ERROR` means there's a bug in the library and this would be truly exceptional. These decisions are judgement calls by the API designer. The important thing is that exceptional conditions (including and especially spurious exceptions that nobody expects) get captured and can be handled higher up the "stack".
+
+So far we have described a way to initiate an asynchronous operation via an API that returns a Future, and then sometime later after it is fulfilled, we get its value. This is slightly more useful than a synchronous API, but it's not yet ideal. There are two more very important pieces to the puzzle.
+
+First, we can aggregate Futures, to define a new Future that completes after some or all of the aggregated Futures complete.  Consider two examples: fetching a batch of requests and waiting for all of them, and fetching a group of requests and waiting for only one of them.
+
+```C++
+vector<Future<GetReply>> futs;
+for (auto& key : keys) {
+  futs.push_back(mc.get(key));
+}
+auto all = whenAll(futs.begin(), futs.end());
+
+vector<Future<GetReply>> futs;
+for (auto& key : keys) {
+  futs.push_back(mc.get(key));
+}
+auto any = whenAny(futs.begin(), futs.end());
+```
+
+`all` and `any` are Futures (for the exact type and usage see the header files).  They will be complete when all/one of `futs` are complete, respectively. (There is also `whenN()` for when you need *some*.)
+
+Second, we can attach callbacks to a Future, and chain them together monadically. An example will clarify:
+
+```C++
+Future<GetReply> fut1 = mc.get("foo");
+
+Future<string> fut2 = fut1.then(
+  [](Try<GetReply>&& t) {
+    if (t.value().result == MemcacheClient::GetReply::Result::FOUND)
+      return t.value().value;
+    throw SomeException("No value");
+  });
+
+Future<void> fut3 = fut2.then(
+  [](Try<string>&& t) {
+    try {
+      cout << t.value() << endl;
+    } catch (std::exception const& e) {
+      cerr << e.what() << endl;
+    }
+  });
+```
+
+That example is a little contrived but the idea is that you can transform a result from one type to another, potentially in a chain, and unhandled errors propagate. Of course, the intermediate variables are optional. `Try<T>` is the object wrapper that supports both value and exception.
+
+Using `then` to add callbacks is idiomatic. It brings all the code into one place, which avoids callback hell.
+
+Up to this point we have skirted around the matter of waiting for Futures. You may never need to wait for a Future, because your code is event-driven and all follow-up action happens in a then-block. But if want to have a batch workflow, where you initiate a batch of asynchronous operations and then wait for them all to finish at a synchronization point, then you will want to wait for a Future.
+
+Other future frameworks like Finagle and std::future/boost::future, give you the ability to wait directly on a Future, by calling `fut.wait()` (naturally enough). Wangle has diverged from this pattern because we don't want to be in the business of dictating how your thread waits. We may work out something that we feel is sufficiently general, in the meantime adapt this spin loop to however your thread should wait:
+
+  while (!f.isReady()) {}
+
+(Hint: you might want to use an event loop or a semaphore or something. You probably don't want to just spin like this.)
+
+Wangle is partially threadsafe. A Promise or Future can migrate between threads as long as there's a full memory barrier of some sort. `Future::then` and `Promise::setValue` (and all variants that boil down to those two calls) can be called from different threads. BUT, be warned that you might be surprised about which thread your callback executes on. Let's consider an example.
+
+```C++
+// Thread A
+Promise<void> p;
+auto f = p.getFuture();
+
+// Thread B
+f.then(x).then(y).then(z);
+
+// Thread A
+p.setValue();
+```
+
+This is legal and technically threadsafe. However, it is important to realize that you do not know in which thread `x`, `y`, and/or `z` will execute. Maybe they will execute in Thread A when `p.setValue()` is called. Or, maybe they will execute in Thread B when `f.then` is called. Or, maybe `x` will execute in Thread B, but `y` and/or `z` will execute in Thread A. There's a race between `setValue` and `then`—whichever runs last will execute the callback. The only guarantee is that one of them will run the callback.
+
+Naturally, you will want some control over which thread executes callbacks. We have a few mechanisms to help.
+
+The first and most useful is `via`, which passes execution through an `Executor`, which usually has the effect of running the callback in a new thread.
+```C++
+aFuture
+  .then(x)
+  .via(e1).then(y1).then(y2)
+  .via(e2).then(z);
+```
+`x` will execute in the current thread. `y1` and `y2` will execute in the thread on the other side of `e1`, and `z` will execute in the thread on the other side of `e2`. `y1` and `y2` will execute on the same thread, whichever thread that is. If `e1` and `e2` execute in different threads than the current thread, then the final callback does not happen in the current thread. If you want to get back to the current thread, you need to get there via an executor.
+
+This works because `via` returns a deactivated ("cold") Future, which blocks the propagation of callbacks until it is activated. Activation happens either explicitly (`activate`) or implicitly when the Future returned by `via` is destructed. In this example, there is no ambiguity about in which context any of the callbacks happen (including `y2`), because propagation is blocked at the `via` callsites until after everything is wired up (temporaries are destructed after the calls to `then` have completed).
+
+You can still have a race after `via` if you break it into multiple statements, e.g. in this counterexample:
+```C++
+f = f.via(e1).then(y1).then(y2); // nothing racy here
+f2.then(y3); // racy
+```
+
+## You make me Promises, Promises
+
+If you are wrapping an asynchronous operation, or providing an asynchronous API to users, then you will want to make Promises. Every Future has a corresponding Promise (except Futures that spring into existence already completed, with `makeFuture()`). Promises are simple, you make one, you extract the Future, and you fulfil it with a value or an exception. Example:
+
+```C++
+Promise<int> p;
+Future<int> f = p.getFuture();
+
+f.isReady() == false
+
+p.setValue(42);
+
+f.isReady() == true
+f.value() == 42
+```
+
+and an exception example:
+
+```C++
+Promise<int> p;
+Future<int> f = p.getFuture();
+
+f.isReady() == false
+
+p.setException(std::runtime_error("Fail"));
+
+f.isReady() == true
+f.value() // throws the exception
+```
+
+It's good practice to use fulfil which takes a function and automatically captures exceptions, e.g.
+
+```C++
+Promise<int> p;
+p.fulfil([]{
+  try {
+    // do stuff that may throw
+    return 42;
+  } catch (MySpecialException const& e) {
+    // handle it
+    return 7;
+  }
+  // Any exceptions that we didn't catch, will be caught for us
+});
+```
+
+## FAQ
+
+### Why not use std::future?
+No callback support.
+See also http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3428.pdf
+
+### Why not use boost::future?
+- 1.53 is brand new, and not in fbcode
+- It's still a bit buggy/bleeding-edge
+- They haven't fleshed out the threading model very well yet, e.g. every single `then` currently spawns a new thread unless you explicitly ask it to work on this thread only, and there is no support for executors yet.
+
+### Why use heap-allocated shared state? Why is Promise not a subclass of Future?
+C++. It boils down to wanting to return a Future by value for performance (move semantics and compiler optimizations), and programmer sanity, and needing a reference to the shared state by both the user (which holds the Future) and the asynchronous operation (which holds the Promise), and allowing either to go out of scope.
+
+### What about proper continuations? Futures suck.
+People mean two things here, they either mean using continuations (as in CSP) or they mean using generators which require continuations. It's important to know those are two distinct questions, but in our context the answer is the same because continuations are a prerequisite for generators.
+
+C++ doesn't directly support continuations very well. But there are some ways to do them in C/C++ that rely on some rather low-level facilities like `setjmp` and `longjmp` (among others). So yes, they are possible (cf. [Mordor](https://github.com/ccutrer/mordor)).
+
+The tradeoff is memory. Each continuation has a stack, and that stack is usually fixed-size and has to be big enough to support whatever ordinary computation you might want to do on it. So each living continuation requires a relatively large amount of memory. If you know the number of continuations will be small, this might be a good fit. In particular, it might be faster and the code might read cleaner.
+
+Wangle takes the middle road between callback hell and continuations, one which has been trodden and proved useful in other languages. It doesn't claim to be the best model for all situations. Use your tools wisely.
diff --git a/folly/wangle/futures/ScheduledExecutor.h b/folly/wangle/futures/ScheduledExecutor.h
new file mode 100644 (file)
index 0000000..94850c2
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Executor.h>
+#include <chrono>
+#include <memory>
+#include <stdexcept>
+
+namespace folly { namespace wangle {
+  // An executor that supports timed scheduling. Like RxScheduler.
+  class ScheduledExecutor : public Executor {
+   public:
+     // Reality is that better than millisecond resolution is very hard to
+     // achieve. However, we reserve the right to be incredible.
+     typedef std::chrono::microseconds Duration;
+     typedef std::chrono::steady_clock::time_point TimePoint;
+
+     virtual ~ScheduledExecutor() = default;
+
+     virtual void add(Func) override = 0;
+
+     /// Alias for add() (for Rx consistency)
+     void schedule(Func&& a) { add(std::move(a)); }
+
+     /// Schedule a Func to be executed after dur time has elapsed
+     /// Expect millisecond resolution at best.
+     void schedule(Func&& a, Duration const& dur) {
+       scheduleAt(std::move(a), now() + dur);
+     }
+
+     /// Schedule a Func to be executed at time t, or as soon afterward as
+     /// possible. Expect millisecond resolution at best. Must be threadsafe.
+     virtual void scheduleAt(Func&& a, TimePoint const& t) {
+       throw std::logic_error("unimplemented");
+     }
+
+     /// Get this executor's notion of time. Must be threadsafe.
+     virtual TimePoint now() {
+       return std::chrono::steady_clock::now();
+     }
+  };
+}}
diff --git a/folly/wangle/futures/Try-inl.h b/folly/wangle/futures/Try-inl.h
new file mode 100644 (file)
index 0000000..44f2cea
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdexcept>
+
+#include <folly/wangle/futures/WangleException.h>
+
+namespace folly { namespace wangle {
+
+template <class T>
+Try<T>::Try(Try<T>&& t) : contains_(t.contains_) {
+  if (contains_ == Contains::VALUE) {
+    new (&value_)T(std::move(t.value_));
+  } else if (contains_ == Contains::EXCEPTION) {
+    new (&e_)std::exception_ptr(t.e_);
+  }
+}
+
+template <class T>
+Try<T>& Try<T>::operator=(Try<T>&& t) {
+  this->~Try();
+  contains_ = t.contains_;
+  if (contains_ == Contains::VALUE) {
+    new (&value_)T(std::move(t.value_));
+  } else if (contains_ == Contains::EXCEPTION) {
+    new (&e_)std::exception_ptr(t.e_);
+  }
+  return *this;
+}
+
+template <class T>
+Try<T>::~Try() {
+  if (contains_ == Contains::VALUE) {
+    value_.~T();
+  } else if (contains_ == Contains::EXCEPTION) {
+    e_.~exception_ptr();
+  }
+}
+
+template <class T>
+T& Try<T>::value() {
+  throwIfFailed();
+  return value_;
+}
+
+template <class T>
+const T& Try<T>::value() const {
+  throwIfFailed();
+  return value_;
+}
+
+template <class T>
+void Try<T>::throwIfFailed() const {
+  if (contains_ != Contains::VALUE) {
+    if (contains_ == Contains::EXCEPTION) {
+      std::rethrow_exception(e_);
+    } else {
+      throw UsingUninitializedTry();
+    }
+  }
+}
+
+void Try<void>::throwIfFailed() const {
+  if (!hasValue_) {
+    std::rethrow_exception(e_);
+  }
+}
+
+template <typename T>
+inline T moveFromTry(wangle::Try<T>&& t) {
+  return std::move(t.value());
+}
+
+inline void moveFromTry(wangle::Try<void>&& t) {
+  return t.value();
+}
+
+template <typename F>
+typename std::enable_if<
+  !std::is_same<typename std::result_of<F()>::type, void>::value,
+  Try<typename std::result_of<F()>::type>>::type
+makeTryFunction(F&& f) {
+  typedef typename std::result_of<F()>::type ResultType;
+  try {
+    auto value = f();
+    return Try<ResultType>(std::move(value));
+  } catch (...) {
+    return Try<ResultType>(std::current_exception());
+  }
+}
+
+template <typename F>
+typename std::enable_if<
+  std::is_same<typename std::result_of<F()>::type, void>::value,
+  Try<void>>::type
+makeTryFunction(F&& f) {
+  try {
+    f();
+    return Try<void>();
+  } catch (...) {
+    return Try<void>(std::current_exception());
+  }
+}
+
+}}
diff --git a/folly/wangle/futures/Try.h b/folly/wangle/futures/Try.h
new file mode 100644 (file)
index 0000000..a90aa4d
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <type_traits>
+#include <exception>
+#include <algorithm>
+#include <folly/Likely.h>
+#include <folly/wangle/futures/WangleException.h>
+
+namespace folly { namespace wangle {
+
+template <class T>
+class Try {
+  static_assert(!std::is_reference<T>::value,
+                "Try may not be used with reference types");
+
+  enum class Contains {
+    VALUE,
+    EXCEPTION,
+    NOTHING,
+  };
+
+ public:
+  typedef T element_type;
+
+  Try() : contains_(Contains::NOTHING) {}
+  explicit Try(const T& v) : contains_(Contains::VALUE), value_(v) {}
+  explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {}
+  explicit Try(std::exception_ptr e) : contains_(Contains::EXCEPTION), e_(e) {}
+
+  // move
+  Try(Try<T>&& t);
+  Try& operator=(Try<T>&& t);
+
+  // no copy
+  Try(const Try<T>& t) = delete;
+  Try& operator=(const Try<T>& t) = delete;
+
+  ~Try();
+
+  T& value();
+  const T& value() const;
+
+  void throwIfFailed() const;
+
+  const T& operator*() const { return value(); }
+        T& operator*()       { return value(); }
+
+  const T* operator->() const { return &value(); }
+        T* operator->()       { return &value(); }
+
+  bool hasValue() const { return contains_ == Contains::VALUE; }
+  bool hasException() const { return contains_ == Contains::EXCEPTION; }
+
+  std::exception_ptr getException() const {
+    if (UNLIKELY(!hasException())) {
+      throw WangleException(
+          "getException(): Try does not contain an exception");
+    }
+    return e_;
+  }
+
+ private:
+  Contains contains_;
+  union {
+    T value_;
+    std::exception_ptr e_;
+  };
+};
+
+template <>
+class Try<void> {
+ public:
+  Try() : hasValue_(true) {}
+  explicit Try(std::exception_ptr e) : hasValue_(false), e_(e) {}
+
+  void value() const { throwIfFailed(); }
+  void operator*() const { return value(); }
+
+  inline void throwIfFailed() const;
+
+  bool hasValue() const { return hasValue_; }
+  bool hasException() const { return !hasValue_; }
+
+  std::exception_ptr getException() const {
+    if (UNLIKELY(!hasException())) {
+      throw WangleException(
+          "getException(): Try does not contain an exception");
+    }
+    return e_;
+  }
+
+ private:
+  bool hasValue_;
+  std::exception_ptr e_;
+};
+
+/**
+ * Extracts value from try and returns it. Throws if try contained an exception.
+ */
+template <typename T>
+T moveFromTry(wangle::Try<T>&& t);
+
+/**
+ * Throws if try contained an exception.
+ */
+void moveFromTry(wangle::Try<void>&& t);
+
+/**
+ * Constructs Try based on the result of execution of function f (e.g. result
+ * or exception).
+ */
+template <typename F>
+typename std::enable_if<
+  !std::is_same<typename std::result_of<F()>::type, void>::value,
+  Try<typename std::result_of<F()>::type>>::type
+makeTryFunction(F&& f);
+
+/**
+ * makeTryFunction specialization for void functions.
+ */
+template <typename F>
+typename std::enable_if<
+  std::is_same<typename std::result_of<F()>::type, void>::value,
+  Try<void>>::type
+makeTryFunction(F&& f);
+
+
+}}
+
+#include <folly/wangle/futures/Try-inl.h>
diff --git a/folly/wangle/futures/WangleException.h b/folly/wangle/futures/WangleException.h
new file mode 100644 (file)
index 0000000..bddf86c
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <string>
+
+namespace folly { namespace wangle {
+
+class WangleException : public std::exception {
+
+public:
+
+  explicit WangleException(std::string message_arg)
+    : message(message_arg) {}
+
+  ~WangleException() throw(){}
+
+  virtual const char *what() const throw() {
+    return message.c_str();
+  }
+
+  bool operator==(const WangleException &other) const{
+    return other.message == this->message;
+  }
+
+  bool operator!=(const WangleException &other) const{
+    return !(*this == other);
+  }
+
+  protected:
+    std::string message;
+};
+
+class BrokenPromise : public WangleException {
+  public:
+    explicit BrokenPromise() :
+      WangleException("Broken promise") { }
+};
+
+class NoState : public WangleException {
+  public:
+    explicit NoState() : WangleException("No state") { }
+};
+
+class PromiseAlreadySatisfied : public WangleException {
+  public:
+    explicit PromiseAlreadySatisfied() :
+      WangleException("Promise already satisfied") { }
+};
+
+class FutureNotReady : public WangleException {
+  public:
+    explicit FutureNotReady() :
+      WangleException("Future not ready") { }
+};
+
+class FutureAlreadyRetrieved : public WangleException {
+  public:
+    explicit FutureAlreadyRetrieved () :
+      WangleException("Future already retrieved") { }
+};
+
+class UsingUninitializedTry : public WangleException {
+  public:
+    explicit UsingUninitializedTry() :
+      WangleException("Using unitialized try") { }
+};
+
+class FutureCancellation : public WangleException {
+ public:
+  FutureCancellation() : WangleException("Future was cancelled") {}
+};
+
+}}
diff --git a/folly/wangle/futures/detail/Core.h b/folly/wangle/futures/detail/Core.h
new file mode 100644 (file)
index 0000000..cd9e5af
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <atomic>
+#include <mutex>
+#include <stdexcept>
+#include <vector>
+
+#include <folly/Optional.h>
+#include <folly/SmallLocks.h>
+
+#include <folly/wangle/futures/Try.h>
+#include <folly/wangle/futures/Promise.h>
+#include <folly/wangle/futures/Future.h>
+#include <folly/Executor.h>
+#include <folly/wangle/futures/detail/FSM.h>
+
+#include <folly/io/async/Request.h>
+
+namespace folly { namespace wangle { namespace detail {
+
+// As of GCC 4.8.1, the std::function in libstdc++ optimizes only for pointers
+// to functions, using a helper avoids a call to malloc.
+template<typename T>
+void empty_callback(Try<T>&&) { }
+
+enum class State {
+  Waiting,
+  Interruptible,
+  Interrupted,
+  Done,
+};
+
+/** The shared state object for Future and Promise. */
+template<typename T>
+class Core : protected FSM<State> {
+ public:
+  // This must be heap-constructed. There's probably a way to enforce that in
+  // code but since this is just internal detail code and I don't know how
+  // off-hand, I'm punting.
+  Core() : FSM<State>(State::Waiting) {}
+  ~Core() {
+    assert(calledBack_);
+    assert(detached_ == 2);
+  }
+
+  // not copyable
+  Core(Core const&) = delete;
+  Core& operator=(Core const&) = delete;
+
+  // not movable (see comment in the implementation of Future::then)
+  Core(Core&&) noexcept = delete;
+  Core& operator=(Core&&) = delete;
+
+  Try<T>& getTry() {
+    if (ready()) {
+      return *result_;
+    } else {
+      throw FutureNotReady();
+    }
+  }
+
+  template <typename F>
+  void setCallback(F func) {
+    auto setCallback_ = [&]{
+      if (callback_) {
+        throw std::logic_error("setCallback called twice");
+      }
+
+      context_ = RequestContext::saveContext();
+      callback_ = std::move(func);
+    };
+
+    FSM_START
+      case State::Waiting:
+      case State::Interruptible:
+      case State::Interrupted:
+        FSM_UPDATE(state, setCallback_);
+        break;
+
+      case State::Done:
+        FSM_UPDATE2(State::Done,
+          setCallback_,
+          [&]{ maybeCallback(); });
+        break;
+    FSM_END
+  }
+
+  void setResult(Try<T>&& t) {
+    FSM_START
+      case State::Waiting:
+      case State::Interruptible:
+      case State::Interrupted:
+        FSM_UPDATE2(State::Done,
+          [&]{ result_ = std::move(t); },
+          [&]{ maybeCallback(); });
+        break;
+
+      case State::Done:
+        throw std::logic_error("setResult called twice");
+    FSM_END
+  }
+
+  bool ready() const {
+    return getState() == State::Done;
+  }
+
+  // Called by a destructing Future
+  void detachFuture() {
+    if (!callback_) {
+      setCallback(empty_callback<T>);
+    }
+    activate();
+    detachOne();
+  }
+
+  // Called by a destructing Promise
+  void detachPromise() {
+    if (!ready()) {
+      setResult(Try<T>(std::make_exception_ptr(BrokenPromise())));
+    }
+    detachOne();
+  }
+
+  void deactivate() {
+    active_ = false;
+  }
+
+  void activate() {
+    active_ = true;
+    if (ready()) {
+      maybeCallback();
+    }
+  }
+
+  bool isActive() { return active_; }
+
+  void setExecutor(Executor* x) {
+    executor_ = x;
+  }
+
+  void raise(std::exception_ptr const& e) {
+    FSM_START
+      case State::Interruptible:
+        FSM_UPDATE2(State::Interrupted,
+          [&]{ interrupt_ = e; },
+          [&]{ interruptHandler_(interrupt_); });
+        break;
+
+      case State::Waiting:
+      case State::Interrupted:
+        FSM_UPDATE(State::Interrupted,
+          [&]{ interrupt_ = e; });
+        break;
+
+      case State::Done:
+        FSM_BREAK
+    FSM_END
+  }
+
+  void setInterruptHandler(std::function<void(std::exception_ptr const&)> fn) {
+    FSM_START
+      case State::Waiting:
+      case State::Interruptible:
+        FSM_UPDATE(State::Interruptible,
+          [&]{ interruptHandler_ = std::move(fn); });
+        break;
+
+      case State::Interrupted:
+        fn(interrupt_);
+        FSM_BREAK
+
+      case State::Done:
+        FSM_BREAK
+    FSM_END
+  }
+
+ private:
+  void maybeCallback() {
+    assert(ready());
+    if (isActive() && callback_) {
+      if (!calledBack_.exchange(true)) {
+        // TODO(5306911) we should probably try/catch
+        Executor* x = executor_;
+
+        RequestContext::setContext(context_);
+        if (x) {
+          MoveWrapper<std::function<void(Try<T>&&)>> cb(std::move(callback_));
+          MoveWrapper<folly::Optional<Try<T>>> val(std::move(result_));
+          x->add([cb, val]() mutable { (*cb)(std::move(**val)); });
+        } else {
+          callback_(std::move(*result_));
+        }
+      }
+    }
+  }
+
+  void detachOne() {
+    auto d = ++detached_;
+    assert(d >= 1);
+    assert(d <= 2);
+    if (d == 2) {
+      // we should have already executed the callback with the value
+      assert(calledBack_);
+      delete this;
+    }
+  }
+
+  folly::Optional<Try<T>> result_;
+  std::function<void(Try<T>&&)> callback_;
+  std::shared_ptr<RequestContext> context_{nullptr};
+  std::atomic<bool> calledBack_ {false};
+  std::atomic<unsigned char> detached_ {0};
+  std::atomic<bool> active_ {true};
+  std::atomic<Executor*> executor_ {nullptr};
+  std::exception_ptr interrupt_;
+  std::function<void(std::exception_ptr const&)> interruptHandler_;
+};
+
+template <typename... Ts>
+struct VariadicContext {
+  VariadicContext() : total(0), count(0) {}
+  Promise<std::tuple<Try<Ts>... > > p;
+  std::tuple<Try<Ts>... > results;
+  size_t total;
+  std::atomic<size_t> count;
+  typedef Future<std::tuple<Try<Ts>...>> type;
+};
+
+template <typename... Ts, typename THead, typename... Fs>
+typename std::enable_if<sizeof...(Fs) == 0, void>::type
+whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
+  head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
+    std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
+    if (++ctx->count == ctx->total) {
+      ctx->p.setValue(std::move(ctx->results));
+      delete ctx;
+    }
+  });
+}
+
+template <typename... Ts, typename THead, typename... Fs>
+typename std::enable_if<sizeof...(Fs) != 0, void>::type
+whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
+  head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
+    std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
+    if (++ctx->count == ctx->total) {
+      ctx->p.setValue(std::move(ctx->results));
+      delete ctx;
+    }
+  });
+  // template tail-recursion
+  whenAllVariadicHelper(ctx, std::forward<Fs>(tail)...);
+}
+
+template <typename T>
+struct WhenAllContext {
+  WhenAllContext() : count(0) {}
+  Promise<std::vector<Try<T> > > p;
+  std::vector<Try<T> > results;
+  std::atomic<size_t> count;
+};
+
+template <typename T>
+struct WhenAnyContext {
+  explicit WhenAnyContext(size_t n) : done(false), ref_count(n) {};
+  Promise<std::pair<size_t, Try<T>>> p;
+  std::atomic<bool> done;
+  std::atomic<size_t> ref_count;
+  void decref() {
+    if (--ref_count == 0) {
+      delete this;
+    }
+  }
+};
+
+}}} // namespace
diff --git a/folly/wangle/futures/detail/Dummy.cpp b/folly/wangle/futures/detail/Dummy.cpp
new file mode 100644 (file)
index 0000000..02a58d4
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// fbbuild is too dumb to know that .h files in the directory affect
+// our project, unless we have a .cpp file in the target, in the same
+// directory.
diff --git a/folly/wangle/futures/detail/FSM.h b/folly/wangle/futures/detail/FSM.h
new file mode 100644 (file)
index 0000000..be4eb8a
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <atomic>
+#include <mutex>
+#include <folly/SmallLocks.h>
+
+namespace folly { namespace wangle { namespace detail {
+
+/// Finite State Machine helper base class.
+/// Inherit from this.
+/// For best results, use an "enum class" for Enum.
+template <class Enum>
+class FSM {
+private:
+  // I am not templatizing this because folly::MicroSpinLock needs to be
+  // zero-initialized (or call init) which isn't generic enough for something
+  // that behaves like std::mutex. :(
+  using Mutex = folly::MicroSpinLock;
+  Mutex mutex_ {0};
+
+  // This might not be necessary for all Enum types, e.g. anything
+  // that is atomically updated in practice on this CPU and there's no risk
+  // of returning a bogus state because of tearing.
+  // An optimization would be to use a static conditional on the Enum type.
+  std::atomic<Enum> state_;
+
+public:
+  explicit FSM(Enum startState) : state_(startState) {}
+
+  Enum getState() const {
+    return state_.load(std::memory_order_relaxed);
+  }
+
+  /// Atomically do a state transition with accompanying action.
+  /// The action will see the old state.
+  /// @returns true on success, false and action unexecuted otherwise
+  template <class F>
+  bool updateState(Enum A, Enum B, F const& action) {
+    std::lock_guard<Mutex> lock(mutex_);
+    if (state_ != A) return false;
+    action();
+    state_ = B;
+    return true;
+  }
+
+  /// Atomically do a state transition with accompanying action. Then do the
+  /// unprotected action without holding the lock. If the atomic transition
+  /// fails, returns false and neither action was executed.
+  ///
+  /// This facilitates code like this:
+  ///   bool done = false;
+  ///   while (!done) {
+  ///     switch (getState()) {
+  ///     case State::Foo:
+  ///       done = updateState(State::Foo, State::Bar,
+  ///           [&]{ /* do protected stuff */ },
+  ///           [&]{ /* do unprotected stuff */});
+  ///       break;
+  ///
+  /// Which reads nicer than code like this:
+  ///   while (true) {
+  ///     switch (getState()) {
+  ///     case State::Foo:
+  ///       if (!updateState(State::Foo, State::Bar,
+  ///           [&]{ /* do protected stuff */ })) {
+  ///         continue;
+  ///       }
+  ///       /* do unprotected stuff */
+  ///       return; // or otherwise break out of the loop
+  ///
+  /// The protected action will see the old state, and the unprotected action
+  /// will see the new state.
+  template <class F1, class F2>
+  bool updateState(Enum A, Enum B,
+                   F1 const& protectedAction, F2 const& unprotectedAction) {
+    bool result = updateState(A, B, protectedAction);
+    if (result) {
+      unprotectedAction();
+    }
+    return result;
+  }
+};
+
+#define FSM_START \
+  {bool done = false; while (!done) { auto state = getState(); switch (state) {
+
+#define FSM_UPDATE2(b, protectedAction, unprotectedAction) \
+    done = updateState(state, (b), (protectedAction), (unprotectedAction));
+
+#define FSM_UPDATE(b, action) FSM_UPDATE2((b), (action), []{})
+
+#define FSM_CASE(a, b, action) \
+  case (a): \
+    FSM_UPDATE((b), (action)); \
+    break;
+
+#define FSM_CASE2(a, b, protectedAction, unprotectedAction) \
+  case (a): \
+    FSM_UPDATE2((b), (protectedAction), (unprotectedAction)); \
+    break;
+
+#define FSM_BREAK done = true; break;
+#define FSM_END }}}
+
+
+}}}
diff --git a/folly/wangle/futures/test/Benchmark.cpp b/folly/wangle/futures/test/Benchmark.cpp
new file mode 100644 (file)
index 0000000..863fa95
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gflags/gflags.h>
+#include <folly/Baton.h>
+#include <folly/Benchmark.h>
+#include <folly/wangle/futures/Future.h>
+#include <folly/wangle/futures/Promise.h>
+#include <semaphore.h>
+#include <vector>
+
+using namespace folly::wangle;
+using namespace std;
+
+namespace {
+
+template <class T>
+T incr(Try<T>&& t) {
+  return t.value() + 1;
+}
+
+void someThens(size_t n) {
+  auto f = makeFuture<int>(42);
+  for (size_t i = 0; i < n; i++) {
+    f = f.then(incr<int>);
+  }
+}
+
+} // anonymous namespace
+
+BENCHMARK(constantFuture) {
+  makeFuture(42);
+}
+
+// This shouldn't get too far below 100%
+BENCHMARK_RELATIVE(promiseAndFuture) {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  p.setValue(42);
+  f.value();
+}
+
+// The higher the better. At the time of writing, it's only about 40% :(
+BENCHMARK_RELATIVE(withThen) {
+  Promise<int> p;
+  Future<int> f = p.getFuture().then(incr<int>);
+  p.setValue(42);
+  f.value();
+}
+
+// thens
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(oneThen) {
+  someThens(1);
+}
+
+// look for >= 50% relative
+BENCHMARK_RELATIVE(twoThens) {
+  someThens(2);
+}
+
+// look for >= 25% relative
+BENCHMARK_RELATIVE(fourThens) {
+  someThens(4);
+}
+
+// look for >= 1% relative
+BENCHMARK_RELATIVE(hundredThens) {
+  someThens(100);
+}
+
+// Lock contention. Although in practice fulfil()s tend to be temporally
+// separate from then()s, still sometimes they will be concurrent. So the
+// higher this number is, the better.
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(no_contention) {
+  vector<Promise<int>> promises(10000);
+  vector<Future<int>> futures;
+  std::thread producer, consumer;
+
+  BENCHMARK_SUSPEND {
+    folly::Baton<> b1, b2;
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    consumer = std::thread([&]{
+      b1.post();
+      for (auto& f : futures) f.then(incr<int>);
+    });
+    consumer.join();
+
+    producer = std::thread([&]{
+      b2.post();
+      for (auto& p : promises) p.setValue(42);
+    });
+
+    b1.wait();
+    b2.wait();
+  }
+
+  // The only thing we are measuring is how long fulfil + callbacks take
+  producer.join();
+}
+
+BENCHMARK_RELATIVE(contention) {
+  vector<Promise<int>> promises(10000);
+  vector<Future<int>> futures;
+  std::thread producer, consumer;
+  sem_t sem;
+  sem_init(&sem, 0, 0);
+
+  BENCHMARK_SUSPEND {
+    folly::Baton<> b1, b2;
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    consumer = std::thread([&]{
+      b1.post();
+      for (auto& f : futures) {
+        sem_wait(&sem);
+        f.then(incr<int>);
+      }
+    });
+
+    producer = std::thread([&]{
+      b2.post();
+      for (auto& p : promises) {
+        sem_post(&sem);
+        p.setValue(42);
+      }
+    });
+
+    b1.wait();
+    b2.wait();
+  }
+
+  // The astute reader will notice that we're not *precisely* comparing apples
+  // to apples here. Well, maybe it's like comparing Granny Smith to
+  // Braeburn or something. In the serial version, we waited for the futures
+  // to be all set up, but here we are probably still doing that work
+  // (although in parallel). But even though there is more work (on the order
+  // of 2x), it is being done by two threads. Hopefully most of the difference
+  // we see is due to lock contention and not false parallelism.
+  //
+  // Be warned that if the box is under heavy load, this will greatly skew
+  // these results (scheduling overhead will begin to dwarf lock contention).
+  // I'm not sure but I'd guess in Windtunnel this will mean large variance,
+  // because I expect they load the boxes as much as they can?
+  consumer.join();
+  producer.join();
+}
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  folly::runBenchmarks();
+  return 0;
+}
diff --git a/folly/wangle/futures/test/ClientCompile.cpp b/folly/wangle/futures/test/ClientCompile.cpp
new file mode 100644 (file)
index 0000000..ab18d12
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// amazing what things can go wrong if you include things in an unexpected
+// order.
+#include <folly/wangle/futures/Try.h>
+#include <folly/wangle/futures/Promise.h>
+#include <folly/wangle/futures/Future.h>
+int main() { return 0; }
diff --git a/folly/wangle/futures/test/ExecutorTest.cpp b/folly/wangle/futures/test/ExecutorTest.cpp
new file mode 100644 (file)
index 0000000..67f43ec
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <folly/wangle/futures/InlineExecutor.h>
+#include <folly/wangle/futures/ManualExecutor.h>
+#include <folly/wangle/futures/QueuedImmediateExecutor.h>
+#include <folly/wangle/futures/Future.h>
+#include <folly/Baton.h>
+
+using namespace folly::wangle;
+using namespace std::chrono;
+using namespace testing;
+
+TEST(ManualExecutor, runIsStable) {
+  ManualExecutor x;
+  size_t count = 0;
+  auto f1 = [&]() { count++; };
+  auto f2 = [&]() { x.add(f1); x.add(f1); };
+  x.add(f2);
+  x.run();
+}
+
+TEST(ManualExecutor, scheduleDur) {
+  ManualExecutor x;
+  size_t count = 0;
+  milliseconds dur {10};
+  x.schedule([&]{ count++; }, dur);
+  EXPECT_EQ(count, 0);
+  x.run();
+  EXPECT_EQ(count, 0);
+  x.advance(dur/2);
+  EXPECT_EQ(count, 0);
+  x.advance(dur/2);
+  EXPECT_EQ(count, 1);
+}
+
+TEST(ManualExecutor, clockStartsAt0) {
+  ManualExecutor x;
+  EXPECT_EQ(x.now(), x.now().min());
+}
+
+TEST(ManualExecutor, scheduleAbs) {
+  ManualExecutor x;
+  size_t count = 0;
+  x.scheduleAt([&]{ count++; }, x.now() + milliseconds(10));
+  EXPECT_EQ(count, 0);
+  x.advance(milliseconds(10));
+  EXPECT_EQ(count, 1);
+}
+
+TEST(ManualExecutor, advanceTo) {
+  ManualExecutor x;
+  size_t count = 0;
+  x.scheduleAt([&]{ count++; }, steady_clock::now());
+  EXPECT_EQ(count, 0);
+  x.advanceTo(steady_clock::now());
+  EXPECT_EQ(count, 1);
+}
+
+TEST(ManualExecutor, advanceBack) {
+  ManualExecutor x;
+  size_t count = 0;
+  x.advance(microseconds(5));
+  x.schedule([&]{ count++; }, microseconds(6));
+  EXPECT_EQ(count, 0);
+  x.advanceTo(x.now() - microseconds(1));
+  EXPECT_EQ(count, 0);
+}
+
+TEST(ManualExecutor, advanceNeg) {
+  ManualExecutor x;
+  size_t count = 0;
+  x.advance(microseconds(5));
+  x.schedule([&]{ count++; }, microseconds(6));
+  EXPECT_EQ(count, 0);
+  x.advance(microseconds(-1));
+  EXPECT_EQ(count, 0);
+}
+
+TEST(ManualExecutor, waitForDoesNotDeadlock) {
+  ManualExecutor east, west;
+  folly::Baton<> baton;
+  auto f = makeFuture()
+    .via(&east)
+    .then([](Try<void>){ return makeFuture(); })
+    .via(&west);
+  std::thread t([&]{
+    baton.post();
+    west.waitFor(f);
+  });
+  baton.wait();
+  east.run();
+  t.join();
+}
+
+TEST(Executor, InlineExecutor) {
+  InlineExecutor x;
+  size_t counter = 0;
+  x.add([&]{
+    x.add([&]{
+      EXPECT_EQ(counter++, 0);
+    });
+    EXPECT_EQ(counter++, 1);
+  });
+  EXPECT_EQ(counter, 2);
+}
+
+TEST(Executor, QueuedImmediateExecutor) {
+  QueuedImmediateExecutor x;
+  size_t counter = 0;
+  x.add([&]{
+    x.add([&]{
+      EXPECT_EQ(1, counter++);
+    });
+    EXPECT_EQ(0, counter++);
+  });
+  EXPECT_EQ(2, counter);
+}
+
+TEST(Executor, Runnable) {
+  InlineExecutor x;
+  size_t counter = 0;
+  struct Runnable {
+    std::function<void()> fn;
+    void operator()() { fn(); }
+  };
+  Runnable f;
+  f.fn = [&]{ counter++; };
+  x.add(f);
+  EXPECT_EQ(counter, 1);
+}
+
+TEST(Executor, RunnablePtr) {
+  InlineExecutor x;
+  struct Runnable {
+    std::function<void()> fn;
+    void operator()() { fn(); }
+  };
+  size_t counter = 0;
+  auto fnp = std::make_shared<Runnable>();
+  fnp->fn = [&]{ counter++; };
+  x.addPtr(fnp);
+  EXPECT_EQ(counter, 1);
+}
diff --git a/folly/wangle/futures/test/FSM.cpp b/folly/wangle/futures/test/FSM.cpp
new file mode 100644 (file)
index 0000000..ff75f86
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <folly/wangle/futures/detail/FSM.h>
+
+using namespace folly::wangle::detail;
+
+enum class State { A, B };
+
+TEST(FSM, example) {
+  FSM<State> fsm(State::A);
+  int count = 0;
+  int unprotectedCount = 0;
+
+  // somebody set up us the switch
+  auto tryTransition = [&]{
+    switch (fsm.getState()) {
+    case State::A:
+      return fsm.updateState(State::A, State::B, [&]{ count++; });
+    case State::B:
+      return fsm.updateState(State::B, State::A,
+                             [&]{ count--; }, [&]{ unprotectedCount--; });
+    }
+    return false; // unreachable
+  };
+
+  // keep retrying until success (like a cas)
+  while (!tryTransition()) ;
+  EXPECT_EQ(State::B, fsm.getState());
+  EXPECT_EQ(1, count);
+  EXPECT_EQ(0, unprotectedCount);
+
+  while (!tryTransition()) ;
+  EXPECT_EQ(State::A, fsm.getState());
+  EXPECT_EQ(0, count);
+  EXPECT_EQ(-1, unprotectedCount);
+}
+
+TEST(FSM, magicMacrosExample) {
+  struct MyFSM : public FSM<State> {
+    int count = 0;
+    int unprotectedCount = 0;
+    MyFSM() : FSM<State>(State::A) {}
+    void twiddle() {
+      FSM_START
+        FSM_CASE(State::A, State::B, [&]{ count++; });
+        FSM_CASE2(State::B, State::A,
+                  [&]{ count--; }, [&]{ unprotectedCount--; });
+      FSM_END
+    }
+  };
+
+  MyFSM fsm;
+
+  fsm.twiddle();
+  EXPECT_EQ(State::B, fsm.getState());
+  EXPECT_EQ(1, fsm.count);
+  EXPECT_EQ(0, fsm.unprotectedCount);
+
+  fsm.twiddle();
+  EXPECT_EQ(State::A, fsm.getState());
+  EXPECT_EQ(0, fsm.count);
+  EXPECT_EQ(-1, fsm.unprotectedCount);
+}
+
+
+TEST(FSM, ctor) {
+  FSM<State> fsm(State::A);
+  EXPECT_EQ(State::A, fsm.getState());
+}
+
+TEST(FSM, update) {
+  FSM<State> fsm(State::A);
+  EXPECT_TRUE(fsm.updateState(State::A, State::B, []{}));
+  EXPECT_EQ(State::B, fsm.getState());
+}
+
+TEST(FSM, badUpdate) {
+  FSM<State> fsm(State::A);
+  EXPECT_FALSE(fsm.updateState(State::B, State::A, []{}));
+}
+
+TEST(FSM, actionOnUpdate) {
+  FSM<State> fsm(State::A);
+  int count = 0;
+  fsm.updateState(State::A, State::B, [&]{ count++; });
+  EXPECT_EQ(1, count);
+}
+
+TEST(FSM, noActionOnBadUpdate) {
+  FSM<State> fsm(State::A);
+  int count = 0;
+  fsm.updateState(State::B, State::A, [&]{ count++; });
+  EXPECT_EQ(0, count);
+}
+
+TEST(FSM, stateTransitionAfterAction) {
+  FSM<State> fsm(State::A);
+  fsm.updateState(State::A, State::B,
+                  [&]{ EXPECT_EQ(State::A, fsm.getState()); });
+}
diff --git a/folly/wangle/futures/test/FutureTest.cpp b/folly/wangle/futures/test/FutureTest.cpp
new file mode 100644 (file)
index 0000000..fedd7a3
--- /dev/null
@@ -0,0 +1,1014 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <atomic>
+#include <folly/small_vector.h>
+#include <gtest/gtest.h>
+#include <memory>
+#include <string>
+#include <thread>
+#include <type_traits>
+#include <unistd.h>
+#include <folly/Memory.h>
+#include <folly/Executor.h>
+#include <folly/wangle/futures/Future.h>
+#include <folly/wangle/futures/ManualExecutor.h>
+#include <folly/MPMCQueue.h>
+
+#include <folly/io/async/Request.h>
+
+using namespace folly;
+using namespace folly::wangle;
+using std::pair;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+#define EXPECT_TYPE(x, T) \
+  EXPECT_TRUE((std::is_same<decltype(x), T>::value))
+
+/// Simple executor that does work in another thread
+class ThreadExecutor : public Executor {
+  folly::MPMCQueue<Func> funcs;
+  std::atomic<bool> done {false};
+  std::thread worker;
+  folly::Baton<> baton;
+
+  void work() {
+    baton.post();
+    Func fn;
+    while (!done) {
+      while (!funcs.isEmpty()) {
+        funcs.blockingRead(fn);
+        fn();
+      }
+    }
+  }
+
+ public:
+  ThreadExecutor(size_t n = 1024)
+    : funcs(n), worker(std::bind(&ThreadExecutor::work, this)) {}
+
+  ~ThreadExecutor() {
+    done = true;
+    funcs.write([]{});
+    worker.join();
+  }
+
+  void add(Func fn) override {
+    funcs.blockingWrite(std::move(fn));
+  }
+
+  void waitForStartup() {
+    baton.wait();
+  }
+};
+
+typedef WangleException eggs_t;
+static eggs_t eggs("eggs");
+
+// Future
+
+TEST(Future, try) {
+  class A {
+   public:
+    A(int x) : x_(x) {}
+
+    int x() const {
+      return x_;
+    }
+   private:
+    int x_;
+  };
+
+  A a(5);
+  Try<A> t_a(std::move(a));
+
+  Try<void> t_void;
+
+  EXPECT_EQ(5, t_a.value().x());
+}
+
+TEST(Future, special) {
+  EXPECT_FALSE(std::is_copy_constructible<Future<int>>::value);
+  EXPECT_FALSE(std::is_copy_assignable<Future<int>>::value);
+  EXPECT_TRUE(std::is_move_constructible<Future<int>>::value);
+  EXPECT_TRUE(std::is_move_assignable<Future<int>>::value);
+}
+
+TEST(Future, thenTry) {
+  bool flag = false;
+
+  makeFuture<int>(42).then([&](Try<int>&& t) {
+                              flag = true;
+                              EXPECT_EQ(42, t.value());
+                            });
+  EXPECT_TRUE(flag); flag = false;
+
+  makeFuture<int>(42)
+    .then([](Try<int>&& t) { return t.value(); })
+    .then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); });
+  EXPECT_TRUE(flag); flag = false;
+
+  makeFuture().then([&](Try<void>&& t) { flag = true; t.value(); });
+  EXPECT_TRUE(flag); flag = false;
+
+  Promise<void> p;
+  auto f = p.getFuture().then([&](Try<void>&& t) { flag = true; });
+  EXPECT_FALSE(flag);
+  EXPECT_FALSE(f.isReady());
+  p.setValue();
+  EXPECT_TRUE(flag);
+  EXPECT_TRUE(f.isReady());
+}
+
+TEST(Future, thenValue) {
+  bool flag = false;
+  makeFuture<int>(42).then([&](int i){
+    EXPECT_EQ(42, i);
+    flag = true;
+  });
+  EXPECT_TRUE(flag); flag = false;
+
+  makeFuture<int>(42)
+    .then([](int i){ return i; })
+    .then([&](int i) { flag = true; EXPECT_EQ(42, i); });
+  EXPECT_TRUE(flag); flag = false;
+
+  makeFuture().then([&]{
+    flag = true;
+  });
+  EXPECT_TRUE(flag); flag = false;
+
+  auto f = makeFuture<int>(eggs).then([&](int i){});
+  EXPECT_THROW(f.value(), eggs_t);
+
+  f = makeFuture<void>(eggs).then([&]{});
+  EXPECT_THROW(f.value(), eggs_t);
+}
+
+TEST(Future, thenValueFuture) {
+  bool flag = false;
+  makeFuture<int>(42)
+    .then([](int i){ return makeFuture<int>(std::move(i)); })
+    .then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); });
+  EXPECT_TRUE(flag); flag = false;
+
+  makeFuture()
+    .then([]{ return makeFuture(); })
+    .then([&](Try<void>&& t) { flag = true; });
+  EXPECT_TRUE(flag); flag = false;
+}
+
+static string doWorkStatic(Try<string>&& t) {
+  return t.value() + ";static";
+}
+
+TEST(Future, thenFunction) {
+  struct Worker {
+    string doWork(Try<string>&& t) {
+      return t.value() + ";class";
+    }
+    static string doWorkStatic(Try<string>&& t) {
+      return t.value() + ";class-static";
+    }
+  } w;
+
+  auto f = makeFuture<string>("start")
+    .then(doWorkStatic)
+    .then(Worker::doWorkStatic)
+    .then(&w, &Worker::doWork);
+
+  EXPECT_EQ(f.value(), "start;static;class-static;class");
+}
+
+static Future<string> doWorkStaticFuture(Try<string>&& t) {
+  return makeFuture(t.value() + ";static");
+}
+
+TEST(Future, thenFunctionFuture) {
+  struct Worker {
+    Future<string> doWorkFuture(Try<string>&& t) {
+      return makeFuture(t.value() + ";class");
+    }
+    static Future<string> doWorkStaticFuture(Try<string>&& t) {
+      return makeFuture(t.value() + ";class-static");
+    }
+  } w;
+
+  auto f = makeFuture<string>("start")
+    .then(doWorkStaticFuture)
+    .then(Worker::doWorkStaticFuture)
+    .then(&w, &Worker::doWorkFuture);
+
+  EXPECT_EQ(f.value(), "start;static;class-static;class");
+}
+
+TEST(Future, value) {
+  auto f = makeFuture(unique_ptr<int>(new int(42)));
+  auto up = std::move(f.value());
+  EXPECT_EQ(42, *up);
+
+  EXPECT_THROW(makeFuture<int>(eggs).value(), eggs_t);
+}
+
+TEST(Future, isReady) {
+  Promise<int> p;
+  auto f = p.getFuture();
+  EXPECT_FALSE(f.isReady());
+  p.setValue(42);
+  EXPECT_TRUE(f.isReady());
+  }
+
+TEST(Future, futureNotReady) {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  EXPECT_THROW(f.value(), eggs_t);
+}
+
+TEST(Future, hasException) {
+  EXPECT_TRUE(makeFuture<int>(eggs).getTry().hasException());
+  EXPECT_FALSE(makeFuture(42).getTry().hasException());
+}
+
+TEST(Future, hasValue) {
+  EXPECT_TRUE(makeFuture(42).getTry().hasValue());
+  EXPECT_FALSE(makeFuture<int>(eggs).getTry().hasValue());
+}
+
+TEST(Future, makeFuture) {
+  EXPECT_TYPE(makeFuture(42), Future<int>);
+  EXPECT_EQ(42, makeFuture(42).value());
+
+  EXPECT_TYPE(makeFuture<float>(42), Future<float>);
+  EXPECT_EQ(42, makeFuture<float>(42).value());
+
+  auto fun = [] { return 42; };
+  EXPECT_TYPE(makeFutureTry(fun), Future<int>);
+  EXPECT_EQ(42, makeFutureTry(fun).value());
+
+  auto failfun = []() -> int { throw eggs; };
+  EXPECT_TYPE(makeFutureTry(failfun), Future<int>);
+  EXPECT_THROW(makeFutureTry(failfun).value(), eggs_t);
+
+  EXPECT_TYPE(makeFuture(), Future<void>);
+}
+
+// Promise
+
+TEST(Promise, special) {
+  EXPECT_FALSE(std::is_copy_constructible<Promise<int>>::value);
+  EXPECT_FALSE(std::is_copy_assignable<Promise<int>>::value);
+  EXPECT_TRUE(std::is_move_constructible<Promise<int>>::value);
+  EXPECT_TRUE(std::is_move_assignable<Promise<int>>::value);
+}
+
+TEST(Promise, getFuture) {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  EXPECT_FALSE(f.isReady());
+}
+
+TEST(Promise, setValue) {
+  Promise<int> fund;
+  auto ffund = fund.getFuture();
+  fund.setValue(42);
+  EXPECT_EQ(42, ffund.value());
+
+  struct Foo {
+    string name;
+    int value;
+  };
+
+  Promise<Foo> pod;
+  auto fpod = pod.getFuture();
+  Foo f = {"the answer", 42};
+  pod.setValue(f);
+  Foo f2 = fpod.value();
+  EXPECT_EQ(f.name, f2.name);
+  EXPECT_EQ(f.value, f2.value);
+
+  pod = Promise<Foo>();
+  fpod = pod.getFuture();
+  pod.setValue(std::move(f2));
+  Foo f3 = fpod.value();
+  EXPECT_EQ(f.name, f3.name);
+  EXPECT_EQ(f.value, f3.value);
+
+  Promise<unique_ptr<int>> mov;
+  auto fmov = mov.getFuture();
+  mov.setValue(unique_ptr<int>(new int(42)));
+  unique_ptr<int> ptr = std::move(fmov.value());
+  EXPECT_EQ(42, *ptr);
+
+  Promise<void> v;
+  auto fv = v.getFuture();
+  v.setValue();
+  EXPECT_TRUE(fv.isReady());
+}
+
+TEST(Promise, setException) {
+  {
+    Promise<void> p;
+    auto f = p.getFuture();
+    p.setException(eggs);
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+  {
+    Promise<void> p;
+    auto f = p.getFuture();
+    try {
+      throw eggs;
+    } catch (...) {
+      p.setException(std::current_exception());
+    }
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+}
+
+TEST(Promise, fulfil) {
+  {
+    Promise<int> p;
+    auto f = p.getFuture();
+    p.fulfil([] { return 42; });
+    EXPECT_EQ(42, f.value());
+  }
+  {
+    Promise<int> p;
+    auto f = p.getFuture();
+    p.fulfil([]() -> int { throw eggs; });
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+}
+
+TEST(Future, finish) {
+  auto x = std::make_shared<int>(0);
+  {
+    Promise<int> p;
+    auto f = p.getFuture().then([x](Try<int>&& t) { *x = t.value(); });
+
+    // The callback hasn't executed
+    EXPECT_EQ(0, *x);
+
+    // The callback has a reference to x
+    EXPECT_EQ(2, x.use_count());
+
+    p.setValue(42);
+
+    // the callback has executed
+    EXPECT_EQ(42, *x);
+  }
+  // the callback has been destructed
+  // and has released its reference to x
+  EXPECT_EQ(1, x.use_count());
+}
+
+TEST(Future, unwrap) {
+  Promise<int> a;
+  Promise<int> b;
+
+  auto fa = a.getFuture();
+  auto fb = b.getFuture();
+
+  bool flag1 = false;
+  bool flag2 = false;
+
+  // do a, then do b, and get the result of a + b.
+  Future<int> f = fa.then([&](Try<int>&& ta) {
+    auto va = ta.value();
+    flag1 = true;
+    return fb.then([va, &flag2](Try<int>&& tb) {
+      flag2 = true;
+      return va + tb.value();
+    });
+  });
+
+  EXPECT_FALSE(flag1);
+  EXPECT_FALSE(flag2);
+  EXPECT_FALSE(f.isReady());
+
+  a.setValue(3);
+  EXPECT_TRUE(flag1);
+  EXPECT_FALSE(flag2);
+  EXPECT_FALSE(f.isReady());
+
+  b.setValue(4);
+  EXPECT_TRUE(flag1);
+  EXPECT_TRUE(flag2);
+  EXPECT_EQ(7, f.value());
+}
+
+TEST(Future, whenAll) {
+  // returns a vector variant
+  {
+    vector<Promise<int>> promises(10);
+    vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = whenAll(futures.begin(), futures.end());
+
+    random_shuffle(promises.begin(), promises.end());
+    for (auto& p : promises) {
+      EXPECT_FALSE(allf.isReady());
+      p.setValue(42);
+    }
+
+    EXPECT_TRUE(allf.isReady());
+    auto& results = allf.value();
+    for (auto& t : results) {
+      EXPECT_EQ(42, t.value());
+    }
+  }
+
+  // check error semantics
+  {
+    vector<Promise<int>> promises(4);
+    vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = whenAll(futures.begin(), futures.end());
+
+
+    promises[0].setValue(42);
+    promises[1].setException(eggs);
+
+    EXPECT_FALSE(allf.isReady());
+
+    promises[2].setValue(42);
+
+    EXPECT_FALSE(allf.isReady());
+
+    promises[3].setException(eggs);
+
+    EXPECT_TRUE(allf.isReady());
+    EXPECT_FALSE(allf.getTry().hasException());
+
+    auto& results = allf.value();
+    EXPECT_EQ(42, results[0].value());
+    EXPECT_TRUE(results[1].hasException());
+    EXPECT_EQ(42, results[2].value());
+    EXPECT_TRUE(results[3].hasException());
+  }
+
+  // check that futures are ready in then()
+  {
+    vector<Promise<void>> promises(10);
+    vector<Future<void>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = whenAll(futures.begin(), futures.end())
+      .then([](Try<vector<Try<void>>>&& ts) {
+        for (auto& f : ts.value())
+          f.value();
+      });
+
+    random_shuffle(promises.begin(), promises.end());
+    for (auto& p : promises)
+      p.setValue();
+    EXPECT_TRUE(allf.isReady());
+  }
+}
+
+
+TEST(Future, whenAny) {
+  {
+    vector<Promise<int>> promises(10);
+    vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    for (auto& f : futures) {
+      EXPECT_FALSE(f.isReady());
+    }
+
+    auto anyf = whenAny(futures.begin(), futures.end());
+
+    /* futures were moved in, so these are invalid now */
+    EXPECT_FALSE(anyf.isReady());
+
+    promises[7].setValue(42);
+    EXPECT_TRUE(anyf.isReady());
+    auto& idx_fut = anyf.value();
+
+    auto i = idx_fut.first;
+    EXPECT_EQ(7, i);
+
+    auto& f = idx_fut.second;
+    EXPECT_EQ(42, f.value());
+  }
+
+  // error
+  {
+    vector<Promise<void>> promises(10);
+    vector<Future<void>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    for (auto& f : futures) {
+      EXPECT_FALSE(f.isReady());
+    }
+
+    auto anyf = whenAny(futures.begin(), futures.end());
+
+    EXPECT_FALSE(anyf.isReady());
+
+    promises[3].setException(eggs);
+    EXPECT_TRUE(anyf.isReady());
+    EXPECT_TRUE(anyf.value().second.hasException());
+  }
+
+  // then()
+  {
+    vector<Promise<int>> promises(10);
+    vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto anyf = whenAny(futures.begin(), futures.end())
+      .then([](Try<pair<size_t, Try<int>>>&& f) {
+        EXPECT_EQ(42, f.value().second.value());
+      });
+
+    promises[3].setValue(42);
+    EXPECT_TRUE(anyf.isReady());
+  }
+}
+
+
+TEST(when, already_completed) {
+  {
+    vector<Future<void>> fs;
+    for (int i = 0; i < 10; i++)
+      fs.push_back(makeFuture());
+
+    whenAll(fs.begin(), fs.end())
+      .then([&](Try<vector<Try<void>>>&& t) {
+        EXPECT_EQ(fs.size(), t.value().size());
+      });
+  }
+  {
+    vector<Future<int>> fs;
+    for (int i = 0; i < 10; i++)
+      fs.push_back(makeFuture(i));
+
+    whenAny(fs.begin(), fs.end())
+      .then([&](Try<pair<size_t, Try<int>>>&& t) {
+        auto& p = t.value();
+        EXPECT_EQ(p.first, p.second.value());
+      });
+  }
+}
+
+TEST(when, whenN) {
+  vector<Promise<void>> promises(10);
+  vector<Future<void>> futures;
+
+  for (auto& p : promises)
+    futures.push_back(p.getFuture());
+
+  bool flag = false;
+  size_t n = 3;
+  whenN(futures.begin(), futures.end(), n)
+    .then([&](Try<vector<pair<size_t, Try<void>>>>&& t) {
+      flag = true;
+      auto v = t.value();
+      EXPECT_EQ(n, v.size());
+      for (auto& tt : v)
+        EXPECT_TRUE(tt.second.hasValue());
+    });
+
+  promises[0].setValue();
+  EXPECT_FALSE(flag);
+  promises[1].setValue();
+  EXPECT_FALSE(flag);
+  promises[2].setValue();
+  EXPECT_TRUE(flag);
+}
+
+/* Ensure that we can compile when_{all,any} with folly::small_vector */
+TEST(when, small_vector) {
+
+  static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future<void>),
+                "Futures should not be trivially copyable");
+  static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future<int>),
+                "Futures should not be trivially copyable");
+
+  using folly::small_vector;
+  {
+    small_vector<Future<void>> futures;
+
+    for (int i = 0; i < 10; i++)
+      futures.push_back(makeFuture());
+
+    auto anyf = whenAny(futures.begin(), futures.end());
+  }
+
+  {
+    small_vector<Future<void>> futures;
+
+    for (int i = 0; i < 10; i++)
+      futures.push_back(makeFuture());
+
+    auto allf = whenAll(futures.begin(), futures.end());
+  }
+}
+
+TEST(Future, whenAllVariadic) {
+  Promise<bool> pb;
+  Promise<int> pi;
+  Future<bool> fb = pb.getFuture();
+  Future<int> fi = pi.getFuture();
+  bool flag = false;
+  whenAll(std::move(fb), std::move(fi))
+    .then([&](Try<std::tuple<Try<bool>, Try<int>>>&& t) {
+      flag = true;
+      EXPECT_TRUE(t.hasValue());
+      EXPECT_TRUE(std::get<0>(t.value()).hasValue());
+      EXPECT_EQ(std::get<0>(t.value()).value(), true);
+      EXPECT_TRUE(std::get<1>(t.value()).hasValue());
+      EXPECT_EQ(std::get<1>(t.value()).value(), 42);
+    });
+  pb.setValue(true);
+  EXPECT_FALSE(flag);
+  pi.setValue(42);
+  EXPECT_TRUE(flag);
+}
+
+TEST(Future, whenAllVariadicReferences) {
+  Promise<bool> pb;
+  Promise<int> pi;
+  Future<bool> fb = pb.getFuture();
+  Future<int> fi = pi.getFuture();
+  bool flag = false;
+  whenAll(fb, fi)
+    .then([&](Try<std::tuple<Try<bool>, Try<int>>>&& t) {
+      flag = true;
+      EXPECT_TRUE(t.hasValue());
+      EXPECT_TRUE(std::get<0>(t.value()).hasValue());
+      EXPECT_EQ(std::get<0>(t.value()).value(), true);
+      EXPECT_TRUE(std::get<1>(t.value()).hasValue());
+      EXPECT_EQ(std::get<1>(t.value()).value(), 42);
+    });
+  pb.setValue(true);
+  EXPECT_FALSE(flag);
+  pi.setValue(42);
+  EXPECT_TRUE(flag);
+}
+
+TEST(Future, whenAll_none) {
+  vector<Future<int>> fs;
+  auto f = whenAll(fs.begin(), fs.end());
+  EXPECT_TRUE(f.isReady());
+}
+
+TEST(Future, throwCaughtInImmediateThen) {
+  // Neither of these should throw "Promise already satisfied"
+  makeFuture().then(
+    [=](Try<void>&&) -> int { throw std::exception(); });
+  makeFuture().then(
+    [=](Try<void>&&) -> Future<int> { throw std::exception(); });
+}
+
+TEST(Future, throwIfFailed) {
+  makeFuture<void>(eggs)
+    .then([=](Try<void>&& t) {
+      EXPECT_THROW(t.throwIfFailed(), eggs_t);
+    });
+  makeFuture()
+    .then([=](Try<void>&& t) {
+      EXPECT_NO_THROW(t.throwIfFailed());
+    });
+
+  makeFuture<int>(eggs)
+    .then([=](Try<int>&& t) {
+      EXPECT_THROW(t.throwIfFailed(), eggs_t);
+    });
+  makeFuture<int>(42)
+    .then([=](Try<int>&& t) {
+      EXPECT_NO_THROW(t.throwIfFailed());
+    });
+}
+
+TEST(Future, waitWithSemaphoreImmediate) {
+  waitWithSemaphore(makeFuture());
+  auto done = waitWithSemaphore(makeFuture(42)).value();
+  EXPECT_EQ(42, done);
+
+  vector<int> v{1,2,3};
+  auto done_v = waitWithSemaphore(makeFuture(v)).value();
+  EXPECT_EQ(v.size(), done_v.size());
+  EXPECT_EQ(v, done_v);
+
+  vector<Future<void>> v_f;
+  v_f.push_back(makeFuture());
+  v_f.push_back(makeFuture());
+  auto done_v_f = waitWithSemaphore(whenAll(v_f.begin(), v_f.end())).value();
+  EXPECT_EQ(2, done_v_f.size());
+
+  vector<Future<bool>> v_fb;
+  v_fb.push_back(makeFuture(true));
+  v_fb.push_back(makeFuture(false));
+  auto fut = whenAll(v_fb.begin(), v_fb.end());
+  auto done_v_fb = std::move(waitWithSemaphore(std::move(fut)).value());
+  EXPECT_EQ(2, done_v_fb.size());
+}
+
+TEST(Future, waitWithSemaphore) {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  std::atomic<bool> flag{false};
+  std::atomic<int> result{1};
+  std::atomic<std::thread::id> id;
+
+  std::thread t([&](Future<int>&& tf){
+      auto n = tf.then([&](Try<int> && t) {
+          id = std::this_thread::get_id();
+          return t.value();
+        });
+      flag = true;
+      result.store(waitWithSemaphore(std::move(n)).value());
+    },
+    std::move(f)
+    );
+  while(!flag){}
+  EXPECT_EQ(result.load(), 1);
+  p.setValue(42);
+  t.join();
+  // validate that the callback ended up executing in this thread, which
+  // is more to ensure that this test actually tests what it should
+  EXPECT_EQ(id, std::this_thread::get_id());
+  EXPECT_EQ(result.load(), 42);
+}
+
+TEST(Future, waitWithSemaphoreForTime) {
+ {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  auto t = waitWithSemaphore(std::move(f),
+    std::chrono::microseconds(1));
+  EXPECT_FALSE(t.isReady());
+  p.setValue(1);
+  EXPECT_TRUE(t.isReady());
+ }
+ {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  p.setValue(1);
+  auto t = waitWithSemaphore(std::move(f),
+    std::chrono::milliseconds(1));
+  EXPECT_TRUE(t.isReady());
+ }
+ {
+  vector<Future<bool>> v_fb;
+  v_fb.push_back(makeFuture(true));
+  v_fb.push_back(makeFuture(false));
+  auto f = whenAll(v_fb.begin(), v_fb.end());
+  auto t = waitWithSemaphore(std::move(f),
+    std::chrono::milliseconds(1));
+  EXPECT_TRUE(t.isReady());
+  EXPECT_EQ(2, t.value().size());
+ }
+ {
+  vector<Future<bool>> v_fb;
+  Promise<bool> p1;
+  Promise<bool> p2;
+  v_fb.push_back(p1.getFuture());
+  v_fb.push_back(p2.getFuture());
+  auto f = whenAll(v_fb.begin(), v_fb.end());
+  auto t = waitWithSemaphore(std::move(f),
+    std::chrono::milliseconds(1));
+  EXPECT_FALSE(t.isReady());
+  p1.setValue(true);
+  EXPECT_FALSE(t.isReady());
+  p2.setValue(true);
+  EXPECT_TRUE(t.isReady());
+ }
+ {
+  auto t = waitWithSemaphore(makeFuture(),
+    std::chrono::milliseconds(1));
+  EXPECT_TRUE(t.isReady());
+ }
+}
+
+TEST(Future, callbackAfterActivate) {
+  Promise<void> p;
+  auto f = p.getFuture();
+  f.deactivate();
+
+  size_t count = 0;
+  f.then([&](Try<void>&&) { count++; });
+
+  p.setValue();
+  EXPECT_EQ(0, count);
+
+  f.activate();
+  EXPECT_EQ(1, count);
+}
+
+TEST(Future, activateOnDestruct) {
+  auto f = std::make_shared<Future<void>>(makeFuture());
+  f->deactivate();
+
+  size_t count = 0;
+  f->then([&](Try<void>&&) { count++; });
+  EXPECT_EQ(0, count);
+
+  f.reset();
+  EXPECT_EQ(1, count);
+}
+
+TEST(Future, viaActsCold) {
+  ManualExecutor x;
+  size_t count = 0;
+
+  auto fv = via(&x);
+  fv.then([&](Try<void>&&) { count++; });
+
+  EXPECT_EQ(0, count);
+
+  fv.activate();
+
+  EXPECT_EQ(1, x.run());
+  EXPECT_EQ(1, count);
+}
+
+TEST(Future, viaIsCold) {
+  ManualExecutor x;
+  EXPECT_FALSE(via(&x).isActive());
+}
+
+TEST(Future, viaRaces) {
+  ManualExecutor x;
+  Promise<void> p;
+  auto tid = std::this_thread::get_id();
+  bool done = false;
+
+  std::thread t1([&] {
+    p.getFuture()
+      .via(&x)
+      .then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
+      .then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
+      .then([&](Try<void>&&) { done = true; });
+  });
+
+  std::thread t2([&] {
+    p.setValue();
+  });
+
+  while (!done) x.run();
+  t1.join();
+  t2.join();
+}
+
+// TODO(#4920689)
+TEST(Future, viaRaces_2stage) {
+  ManualExecutor x;
+  Promise<void> p;
+  auto tid = std::this_thread::get_id();
+  bool done = false;
+
+  std::thread t1([&] {
+    auto f2 = p.getFuture().via(&x);
+    f2.then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
+      .then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
+      .then([&](Try<void>&&) { done = true; });
+
+    // the bug was in the promise being fulfilled before f2 is reactivated. we
+    // could sleep, but yielding should cause this to fail with reasonable
+    // probability
+    std::this_thread::yield();
+    f2.activate();
+  });
+
+  std::thread t2([&] {
+    p.setValue();
+  });
+
+  while (!done) x.run();
+  t1.join();
+  t2.join();
+}
+
+TEST(Future, getFuture_after_setValue) {
+  Promise<int> p;
+  p.setValue(42);
+  EXPECT_EQ(42, p.getFuture().value());
+}
+
+TEST(Future, getFuture_after_setException) {
+  Promise<void> p;
+  p.fulfil([]() -> void { throw std::logic_error("foo"); });
+  EXPECT_THROW(p.getFuture().value(), std::logic_error);
+}
+
+TEST(Future, detachRace) {
+  // Task #5438209
+  // This test is designed to detect a race that was in Core::detachOne()
+  // where detached_ was incremented and then tested, and that
+  // allowed a race where both Promise and Future would think they were the
+  // second and both try to delete. This showed up at scale but was very
+  // difficult to reliably repro in a test. As it is, this only fails about
+  // once in every 1,000 executions. Doing this 1,000 times is going to make a
+  // slow test so I won't do that but if it ever fails, take it seriously, and
+  // run the test binary with "--gtest_repeat=10000 --gtest_filter=*detachRace"
+  // (Don't forget to enable ASAN)
+  auto p = folly::make_unique<Promise<bool>>();
+  auto f = folly::make_unique<Future<bool>>(p->getFuture());
+  folly::Baton<> baton;
+  std::thread t1([&]{
+    baton.post();
+    p.reset();
+  });
+  baton.wait();
+  f.reset();
+  t1.join();
+}
+
+class TestData : public RequestData {
+ public:
+  explicit TestData(int data) : data_(data) {}
+  virtual ~TestData() {}
+  int data_;
+};
+
+TEST(Future, context) {
+
+  // Start a new context
+  RequestContext::create();
+
+  EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test"));
+
+  // Set some test data
+  RequestContext::get()->setContextData(
+    "test",
+    std::unique_ptr<TestData>(new TestData(10)));
+
+  // Start a future
+  Promise<void> p;
+  auto future = p.getFuture().then([&]{
+    // Check that the context followed the future
+    EXPECT_TRUE(RequestContext::get() != nullptr);
+    auto a = dynamic_cast<TestData*>(
+      RequestContext::get()->getContextData("test"));
+    auto data = a->data_;
+    EXPECT_EQ(10, data);
+  });
+
+  // Clear the context
+  RequestContext::setContext(nullptr);
+
+  EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test"));
+
+  // Fulfil the promise
+  p.setValue();
+}
+
+
+// This only fails about 1 in 1k times when the bug is present :(
+TEST(Future, t5506504) {
+  ThreadExecutor x;
+
+  auto fn = [&x]{
+    auto promises = std::make_shared<vector<Promise<void>>>(4);
+    vector<Future<void>> futures;
+
+    for (auto& p : *promises) {
+      futures.emplace_back(
+        p.getFuture()
+        .via(&x)
+        .then([](Try<void>&&){}));
+    }
+
+    x.waitForStartup();
+    x.add([promises]{
+      for (auto& p : *promises) p.setValue();
+    });
+
+    return whenAll(futures.begin(), futures.end());
+  };
+
+  waitWithSemaphore(fn());
+}
diff --git a/folly/wangle/futures/test/Interrupts.cpp b/folly/wangle/futures/test/Interrupts.cpp
new file mode 100644 (file)
index 0000000..3ecf050
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <folly/wangle/futures/Future.h>
+#include <folly/wangle/futures/Promise.h>
+
+using namespace folly::wangle;
+
+TEST(Interrupts, raise) {
+  std::runtime_error eggs("eggs");
+  Promise<void> p;
+  p.setInterruptHandler([&](std::exception_ptr e) {
+    EXPECT_THROW(std::rethrow_exception(e), decltype(eggs));
+  });
+  p.getFuture().raise(eggs);
+}
+
+TEST(Interrupts, cancel) {
+  Promise<void> p;
+  p.setInterruptHandler([&](std::exception_ptr e) {
+    EXPECT_THROW(std::rethrow_exception(e), FutureCancellation);
+  });
+  p.getFuture().cancel();
+}
+
+TEST(Interrupts, handleThenInterrupt) {
+  Promise<int> p;
+  bool flag = false;
+  p.setInterruptHandler([&](std::exception_ptr e) { flag = true; });
+  p.getFuture().cancel();
+  EXPECT_TRUE(flag);
+}
+
+TEST(Interrupts, interruptThenHandle) {
+  Promise<int> p;
+  bool flag = false;
+  p.getFuture().cancel();
+  p.setInterruptHandler([&](std::exception_ptr e) { flag = true; });
+  EXPECT_TRUE(flag);
+}
+
+TEST(Interrupts, interruptAfterFulfilNoop) {
+  Promise<void> p;
+  bool flag = false;
+  p.setInterruptHandler([&](std::exception_ptr e) { flag = true; });
+  p.setValue();
+  p.getFuture().cancel();
+  EXPECT_FALSE(flag);
+}
+
+TEST(Interrupts, secondInterruptNoop) {
+  Promise<void> p;
+  int count = 0;
+  p.setInterruptHandler([&](std::exception_ptr e) { count++; });
+  auto f = p.getFuture();
+  f.cancel();
+  f.cancel();
+  EXPECT_EQ(1, count);
+}
diff --git a/folly/wangle/futures/test/Thens.cpp b/folly/wangle/futures/test/Thens.cpp
new file mode 100644 (file)
index 0000000..2ebbf48
--- /dev/null
@@ -0,0 +1,34 @@
+// This file is @generated by thens.rb. Do not edit directly.
+
+// TODO: fails to compile with clang:dev.  See task #4412111
+#ifndef __clang__
+
+#include <folly/wangle/futures/test/Thens.h>
+
+TEST(Future, thenVariants) {
+  SomeClass anObject;
+  folly::Executor* anExecutor;
+
+  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A>&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, Try<A>&&>);}
+  {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, Try<A>&&>);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A>&&>());}
+  {Future<B> f = someFuture<A>().then([&](Try<A>&&){return someFuture<B>();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, A&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, A&&>);}
+  {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, A&&>);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, A&&>());}
+  {Future<B> f = someFuture<A>().then([&](A&&){return someFuture<B>();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<B, Try<A>&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A>&&>);}
+  {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, Try<A>&&>);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A>&&>());}
+  {Future<B> f = someFuture<A>().then([&](Try<A>&&){return B();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<B, A&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, A&&>);}
+  {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, A&&>);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<B, A&&>());}
+  {Future<B> f = someFuture<A>().then([&](A&&){return B();});}
+}
+
+#endif
diff --git a/folly/wangle/futures/test/Thens.h b/folly/wangle/futures/test/Thens.h
new file mode 100644 (file)
index 0000000..d039848
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <gtest/gtest.h>
+#include <memory>
+#include <folly/wangle/futures/Future.h>
+#include <folly/Executor.h>
+
+using namespace folly::wangle;
+using namespace std;
+using namespace testing;
+
+typedef unique_ptr<int> A;
+struct B {};
+
+template <class T>
+using EnableIfFuture = typename std::enable_if<isFuture<T>::value>::type;
+
+template <class T>
+using EnableUnlessFuture = typename std::enable_if<!isFuture<T>::value>::type;
+
+template <class T>
+Future<T> someFuture() {
+  return makeFuture(T());
+}
+
+template <class Ret, class... Params, typename = void>
+Ret aFunction(Params...);
+
+template <class Ret, class... Params>
+typename std::enable_if<isFuture<Ret>::value, Ret>::type
+aFunction(Params...) {
+  typedef typename Ret::value_type T;
+  return makeFuture(T());
+}
+
+template <class Ret, class... Params>
+typename std::enable_if<!isFuture<Ret>::value, Ret>::type
+aFunction(Params...) {
+  return Ret();
+}
+
+template <class Ret, class... Params>
+std::function<Ret(Params...)>
+aStdFunction(
+    typename std::enable_if<!isFuture<Ret>::value, bool>::type = false) {
+  return [](Params...) -> Ret { return Ret(); };
+}
+
+template <class Ret, class... Params>
+std::function<Ret(Params...)>
+aStdFunction(typename std::enable_if<isFuture<Ret>::value, bool>::type = true) {
+  typedef typename Ret::value_type T;
+  return [](Params...) -> Future<T> { return makeFuture(T()); };
+}
+
+class SomeClass {
+  B b;
+public:
+  template <class Ret, class... Params>
+  static Ret aStaticMethod(Params...);
+
+  template <class Ret, class... Params>
+  static
+  typename std::enable_if<!isFuture<Ret>::value, Ret>::type
+  aStaticMethod(Params...) {
+    return Ret();
+  }
+
+  template <class Ret, class... Params>
+  static
+  typename std::enable_if<isFuture<Ret>::value, Ret>::type
+  aStaticMethod(Params...) {
+    typedef typename Ret::value_type T;
+    return makeFuture(T());
+  }
+
+  template <class Ret, class... Params>
+  Ret aMethod(Params...);
+
+  template <class Ret, class... Params>
+  typename std::enable_if<!isFuture<Ret>::value, Ret>::type
+  aMethod(Params...) {
+    return Ret();
+  }
+
+  template <class Ret, class... Params>
+  typename std::enable_if<isFuture<Ret>::value, Ret>::type
+  aMethod(Params...) {
+    typedef typename Ret::value_type T;
+    return makeFuture(T());
+  }
+};
diff --git a/folly/wangle/futures/test/Try.cpp b/folly/wangle/futures/test/Try.cpp
new file mode 100644 (file)
index 0000000..2c8a606
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <folly/Memory.h>
+#include <folly/wangle/futures/Try.h>
+
+using namespace folly::wangle;
+
+TEST(Try, makeTryFunction) {
+  auto func = []() {
+    return folly::make_unique<int>(1);
+  };
+
+  auto result = makeTryFunction(func);
+  EXPECT_TRUE(result.hasValue());
+  EXPECT_EQ(*result.value(), 1);
+}
+
+TEST(Try, makeTryFunctionThrow) {
+  auto func = []() {
+    throw std::runtime_error("Runtime");
+    return folly::make_unique<int>(1);
+  };
+
+  auto result = makeTryFunction(func);
+  EXPECT_TRUE(result.hasException());
+}
+
+TEST(Try, makeTryFunctionVoid) {
+  auto func = []() {
+    return;
+  };
+
+  auto result = makeTryFunction(func);
+  EXPECT_TRUE(result.hasValue());
+}
+
+TEST(Try, makeTryFunctionVoidThrow) {
+  auto func = []() {
+    throw std::runtime_error("Runtime");
+    return;
+  };
+
+  auto result = makeTryFunction(func);
+  EXPECT_TRUE(result.hasException());
+}
diff --git a/folly/wangle/futures/test/ViaTest.cpp b/folly/wangle/futures/test/ViaTest.cpp
new file mode 100644 (file)
index 0000000..80765ff
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <thread>
+
+#include <folly/wangle/futures/Future.h>
+#include <folly/wangle/futures/InlineExecutor.h>
+#include <folly/wangle/futures/ManualExecutor.h>
+
+using namespace folly::wangle;
+
+struct ManualWaiter {
+  explicit ManualWaiter(std::shared_ptr<ManualExecutor> ex) : ex(ex) {}
+
+  void makeProgress() {
+    ex->wait();
+    ex->run();
+  }
+
+  std::shared_ptr<ManualExecutor> ex;
+};
+
+struct ViaFixture : public testing::Test {
+  ViaFixture() :
+    westExecutor(new ManualExecutor),
+    eastExecutor(new ManualExecutor),
+    waiter(new ManualWaiter(westExecutor)),
+    done(false)
+  {
+    t = std::thread([=] {
+      ManualWaiter eastWaiter(eastExecutor);
+      while (!done)
+        eastWaiter.makeProgress();
+      });
+  }
+
+  ~ViaFixture() {
+    done = true;
+    eastExecutor->add([=]() { });
+    t.join();
+  }
+
+  void addAsync(int a, int b, std::function<void(int&&)>&& cob) {
+    eastExecutor->add([=]() {
+      cob(a + b);
+    });
+  }
+
+  std::shared_ptr<ManualExecutor> westExecutor;
+  std::shared_ptr<ManualExecutor> eastExecutor;
+  std::shared_ptr<ManualWaiter> waiter;
+  InlineExecutor inlineExecutor;
+  bool done;
+  std::thread t;
+};
+
+TEST(Via, exception_on_launch) {
+  auto future = makeFuture<int>(std::runtime_error("E"));
+  EXPECT_THROW(future.value(), std::runtime_error);
+}
+
+TEST(Via, then_value) {
+  auto future = makeFuture(std::move(1))
+    .then([](Try<int>&& t) {
+      return t.value() == 1;
+    })
+    ;
+
+  EXPECT_TRUE(future.value());
+}
+
+TEST(Via, then_future) {
+  auto future = makeFuture(1)
+    .then([](Try<int>&& t) {
+      return makeFuture(t.value() == 1);
+    })
+    ;
+  EXPECT_TRUE(future.value());
+}
+
+static Future<std::string> doWorkStatic(Try<std::string>&& t) {
+  return makeFuture(t.value() + ";static");
+}
+
+TEST(Via, then_function) {
+  struct Worker {
+    Future<std::string> doWork(Try<std::string>&& t) {
+      return makeFuture(t.value() + ";class");
+    }
+    static Future<std::string> doWorkStatic(Try<std::string>&& t) {
+      return makeFuture(t.value() + ";class-static");
+    }
+  } w;
+
+  auto f = makeFuture(std::string("start"))
+    .then(doWorkStatic)
+    .then(Worker::doWorkStatic)
+    .then(&w, &Worker::doWork)
+    ;
+
+  EXPECT_EQ(f.value(), "start;static;class-static;class");
+}
+
+TEST_F(ViaFixture, deactivateChain) {
+  bool flag = false;
+  auto f = makeFuture().deactivate();
+  EXPECT_FALSE(f.isActive());
+  auto f2 = f.then([&](Try<void>){ flag = true; });
+  EXPECT_FALSE(flag);
+}
+
+TEST_F(ViaFixture, deactivateActivateChain) {
+  bool flag = false;
+  // you can do this all day long with temporaries.
+  auto f1 = makeFuture().deactivate().activate().deactivate();
+  // Chaining on activate/deactivate requires an rvalue, so you have to move
+  // one of these two ways (if you're not using a temporary).
+  auto f2 = std::move(f1).activate();
+  f2.deactivate();
+  auto f3 = std::move(f2.activate());
+  f3.then([&](Try<void>){ flag = true; });
+  EXPECT_TRUE(flag);
+}
+
+TEST_F(ViaFixture, thread_hops) {
+  auto westThreadId = std::this_thread::get_id();
+  auto f = via(eastExecutor.get()).then([=](Try<void>&& t) {
+    EXPECT_NE(std::this_thread::get_id(), westThreadId);
+    return makeFuture<int>(1);
+  }).via(westExecutor.get()
+  ).then([=](Try<int>&& t) {
+    EXPECT_EQ(std::this_thread::get_id(), westThreadId);
+    return t.value();
+  });
+  while (!f.isReady()) {
+    waiter->makeProgress();
+  }
+  EXPECT_EQ(f.value(), 1);
+}
+
+TEST_F(ViaFixture, chain_vias) {
+  auto westThreadId = std::this_thread::get_id();
+  auto f = via(eastExecutor.get()).then([=](Try<void>&& t) {
+    EXPECT_NE(std::this_thread::get_id(), westThreadId);
+    return makeFuture<int>(1);
+  }).then([=](Try<int>&& t) {
+    int val = t.value();
+    return makeFuture(std::move(val)).via(westExecutor.get())
+      .then([=](Try<int>&& t) mutable {
+        EXPECT_EQ(std::this_thread::get_id(), westThreadId);
+        return t.value();
+      });
+  }).then([=](Try<int>&& t) {
+    EXPECT_EQ(std::this_thread::get_id(), westThreadId);
+    return t.value();
+  });
+
+  while (!f.isReady()) {
+    waiter->makeProgress();
+  }
+  EXPECT_EQ(f.value(), 1);
+}
+
+TEST_F(ViaFixture, bareViaAssignment) {
+  auto f = via(eastExecutor.get());
+}
+TEST_F(ViaFixture, viaAssignment) {
+  // via()&&
+  auto f = makeFuture().via(eastExecutor.get());
+  // via()&
+  auto f2 = f.via(eastExecutor.get());
+}
diff --git a/folly/wangle/futures/test/main.cpp b/folly/wangle/futures/test/main.cpp
new file mode 100644 (file)
index 0000000..7dbf27d
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/folly/wangle/futures/test/thens.rb b/folly/wangle/futures/test/thens.rb
new file mode 100755 (executable)
index 0000000..4aaa846
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/env ruby
+
+# ruby thens.rb > Thens.cpp
+
+# An exercise in combinatorics.
+# (ordinary/static function, member function, std::function, lambda)
+# X
+# returns (Future<R>, R)
+# X
+# accepts (Try<T>&&, Try<T> const&, Try<T>, T&&, T const&, T, nothing)
+
+def test(*args)
+  args = args.join(", ")
+  [
+  "{Future<B> f = someFuture<A>().then(#{args});}",
+  #"{Future<B> f = makeFuture(A()).then(#{args}, anExecutor);}",
+  ]
+end
+
+def retval(ret)
+  {
+    "Future<B>" => "someFuture<B>()",
+    "Try<B>" => "Try<B>(B())",
+    "B" => "B()"
+  }[ret]
+end
+
+return_types = [
+  "Future<B>",
+  "B",
+  #"Try<B>",
+]
+param_types = [
+    "Try<A>&&",
+    #"Try<A> const&",
+    #"Try<A>",
+    #"Try<A>&",
+    "A&&",
+    #"A const&",
+    #"A",
+    #"A&",
+    #"",
+  ]
+
+tests = (
+  return_types.map { |ret|
+    param_types.map { |param|
+      both = "#{ret}, #{param}"
+      [
+        ["&aFunction<#{both}>"],
+        ["&SomeClass::aStaticMethod<#{both}>"],
+        # TODO switch these around (std::bind-style)
+        ["&anObject", "&SomeClass::aMethod<#{both}>"],
+        ["aStdFunction<#{both}>()"],
+        ["[&](#{param}){return #{retval(ret)};}"],
+      ]
+    }
+  }.flatten(2) + [
+    #[""],
+  ]
+).map {|a| test(a)}.flatten
+
+print <<EOF
+// This file is #{"@"}generated by thens.rb. Do not edit directly.
+
+// TODO: fails to compile with clang:dev.  See task #4412111
+#ifndef __clang__
+
+#include <folly/wangle/futures/test/Thens.h>
+
+TEST(Future, thenVariants) {
+  SomeClass anObject;
+  folly::Executor* anExecutor;
+
+  #{tests.join("\n  ")}
+}
+
+#endif
+EOF
diff --git a/folly/wangle/test/Benchmark.cpp b/folly/wangle/test/Benchmark.cpp
deleted file mode 100644 (file)
index 95fcef9..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gflags/gflags.h>
-#include <folly/Baton.h>
-#include <folly/Benchmark.h>
-#include <folly/wangle/Future.h>
-#include <folly/wangle/Promise.h>
-#include <semaphore.h>
-#include <vector>
-
-using namespace folly::wangle;
-using namespace std;
-
-namespace {
-
-template <class T>
-T incr(Try<T>&& t) {
-  return t.value() + 1;
-}
-
-void someThens(size_t n) {
-  auto f = makeFuture<int>(42);
-  for (size_t i = 0; i < n; i++) {
-    f = f.then(incr<int>);
-  }
-}
-
-} // anonymous namespace
-
-BENCHMARK(constantFuture) {
-  makeFuture(42);
-}
-
-// This shouldn't get too far below 100%
-BENCHMARK_RELATIVE(promiseAndFuture) {
-  Promise<int> p;
-  Future<int> f = p.getFuture();
-  p.setValue(42);
-  f.value();
-}
-
-// The higher the better. At the time of writing, it's only about 40% :(
-BENCHMARK_RELATIVE(withThen) {
-  Promise<int> p;
-  Future<int> f = p.getFuture().then(incr<int>);
-  p.setValue(42);
-  f.value();
-}
-
-// thens
-BENCHMARK_DRAW_LINE()
-
-BENCHMARK(oneThen) {
-  someThens(1);
-}
-
-// look for >= 50% relative
-BENCHMARK_RELATIVE(twoThens) {
-  someThens(2);
-}
-
-// look for >= 25% relative
-BENCHMARK_RELATIVE(fourThens) {
-  someThens(4);
-}
-
-// look for >= 1% relative
-BENCHMARK_RELATIVE(hundredThens) {
-  someThens(100);
-}
-
-// Lock contention. Although in practice fulfil()s tend to be temporally
-// separate from then()s, still sometimes they will be concurrent. So the
-// higher this number is, the better.
-BENCHMARK_DRAW_LINE()
-
-BENCHMARK(no_contention) {
-  vector<Promise<int>> promises(10000);
-  vector<Future<int>> futures;
-  std::thread producer, consumer;
-
-  BENCHMARK_SUSPEND {
-    folly::Baton<> b1, b2;
-    for (auto& p : promises)
-      futures.push_back(p.getFuture());
-
-    consumer = std::thread([&]{
-      b1.post();
-      for (auto& f : futures) f.then(incr<int>);
-    });
-    consumer.join();
-
-    producer = std::thread([&]{
-      b2.post();
-      for (auto& p : promises) p.setValue(42);
-    });
-
-    b1.wait();
-    b2.wait();
-  }
-
-  // The only thing we are measuring is how long fulfil + callbacks take
-  producer.join();
-}
-
-BENCHMARK_RELATIVE(contention) {
-  vector<Promise<int>> promises(10000);
-  vector<Future<int>> futures;
-  std::thread producer, consumer;
-  sem_t sem;
-  sem_init(&sem, 0, 0);
-
-  BENCHMARK_SUSPEND {
-    folly::Baton<> b1, b2;
-    for (auto& p : promises)
-      futures.push_back(p.getFuture());
-
-    consumer = std::thread([&]{
-      b1.post();
-      for (auto& f : futures) {
-        sem_wait(&sem);
-        f.then(incr<int>);
-      }
-    });
-
-    producer = std::thread([&]{
-      b2.post();
-      for (auto& p : promises) {
-        sem_post(&sem);
-        p.setValue(42);
-      }
-    });
-
-    b1.wait();
-    b2.wait();
-  }
-
-  // The astute reader will notice that we're not *precisely* comparing apples
-  // to apples here. Well, maybe it's like comparing Granny Smith to
-  // Braeburn or something. In the serial version, we waited for the futures
-  // to be all set up, but here we are probably still doing that work
-  // (although in parallel). But even though there is more work (on the order
-  // of 2x), it is being done by two threads. Hopefully most of the difference
-  // we see is due to lock contention and not false parallelism.
-  //
-  // Be warned that if the box is under heavy load, this will greatly skew
-  // these results (scheduling overhead will begin to dwarf lock contention).
-  // I'm not sure but I'd guess in Windtunnel this will mean large variance,
-  // because I expect they load the boxes as much as they can?
-  consumer.join();
-  producer.join();
-}
-
-int main(int argc, char** argv) {
-  gflags::ParseCommandLineFlags(&argc, &argv, true);
-  folly::runBenchmarks();
-  return 0;
-}
diff --git a/folly/wangle/test/ClientCompile.cpp b/folly/wangle/test/ClientCompile.cpp
deleted file mode 100644 (file)
index 45ad8cd..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// amazing what things can go wrong if you include things in an unexpected
-// order.
-#include <folly/wangle/Try.h>
-#include <folly/wangle/Promise.h>
-#include <folly/wangle/Future.h>
-int main() { return 0; }
diff --git a/folly/wangle/test/ExecutorTest.cpp b/folly/wangle/test/ExecutorTest.cpp
deleted file mode 100644 (file)
index b4ec768..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <folly/wangle/InlineExecutor.h>
-#include <folly/wangle/ManualExecutor.h>
-#include <folly/wangle/QueuedImmediateExecutor.h>
-#include <folly/wangle/Future.h>
-#include <folly/Baton.h>
-
-using namespace folly::wangle;
-using namespace std::chrono;
-using namespace testing;
-
-TEST(ManualExecutor, runIsStable) {
-  ManualExecutor x;
-  size_t count = 0;
-  auto f1 = [&]() { count++; };
-  auto f2 = [&]() { x.add(f1); x.add(f1); };
-  x.add(f2);
-  x.run();
-}
-
-TEST(ManualExecutor, scheduleDur) {
-  ManualExecutor x;
-  size_t count = 0;
-  milliseconds dur {10};
-  x.schedule([&]{ count++; }, dur);
-  EXPECT_EQ(count, 0);
-  x.run();
-  EXPECT_EQ(count, 0);
-  x.advance(dur/2);
-  EXPECT_EQ(count, 0);
-  x.advance(dur/2);
-  EXPECT_EQ(count, 1);
-}
-
-TEST(ManualExecutor, clockStartsAt0) {
-  ManualExecutor x;
-  EXPECT_EQ(x.now(), x.now().min());
-}
-
-TEST(ManualExecutor, scheduleAbs) {
-  ManualExecutor x;
-  size_t count = 0;
-  x.scheduleAt([&]{ count++; }, x.now() + milliseconds(10));
-  EXPECT_EQ(count, 0);
-  x.advance(milliseconds(10));
-  EXPECT_EQ(count, 1);
-}
-
-TEST(ManualExecutor, advanceTo) {
-  ManualExecutor x;
-  size_t count = 0;
-  x.scheduleAt([&]{ count++; }, steady_clock::now());
-  EXPECT_EQ(count, 0);
-  x.advanceTo(steady_clock::now());
-  EXPECT_EQ(count, 1);
-}
-
-TEST(ManualExecutor, advanceBack) {
-  ManualExecutor x;
-  size_t count = 0;
-  x.advance(microseconds(5));
-  x.schedule([&]{ count++; }, microseconds(6));
-  EXPECT_EQ(count, 0);
-  x.advanceTo(x.now() - microseconds(1));
-  EXPECT_EQ(count, 0);
-}
-
-TEST(ManualExecutor, advanceNeg) {
-  ManualExecutor x;
-  size_t count = 0;
-  x.advance(microseconds(5));
-  x.schedule([&]{ count++; }, microseconds(6));
-  EXPECT_EQ(count, 0);
-  x.advance(microseconds(-1));
-  EXPECT_EQ(count, 0);
-}
-
-TEST(ManualExecutor, waitForDoesNotDeadlock) {
-  ManualExecutor east, west;
-  folly::Baton<> baton;
-  auto f = makeFuture()
-    .via(&east)
-    .then([](Try<void>){ return makeFuture(); })
-    .via(&west);
-  std::thread t([&]{
-    baton.post();
-    west.waitFor(f);
-  });
-  baton.wait();
-  east.run();
-  t.join();
-}
-
-TEST(Executor, InlineExecutor) {
-  InlineExecutor x;
-  size_t counter = 0;
-  x.add([&]{
-    x.add([&]{
-      EXPECT_EQ(counter++, 0);
-    });
-    EXPECT_EQ(counter++, 1);
-  });
-  EXPECT_EQ(counter, 2);
-}
-
-TEST(Executor, QueuedImmediateExecutor) {
-  QueuedImmediateExecutor x;
-  size_t counter = 0;
-  x.add([&]{
-    x.add([&]{
-      EXPECT_EQ(1, counter++);
-    });
-    EXPECT_EQ(0, counter++);
-  });
-  EXPECT_EQ(2, counter);
-}
-
-TEST(Executor, Runnable) {
-  InlineExecutor x;
-  size_t counter = 0;
-  struct Runnable {
-    std::function<void()> fn;
-    void operator()() { fn(); }
-  };
-  Runnable f;
-  f.fn = [&]{ counter++; };
-  x.add(f);
-  EXPECT_EQ(counter, 1);
-}
-
-TEST(Executor, RunnablePtr) {
-  InlineExecutor x;
-  struct Runnable {
-    std::function<void()> fn;
-    void operator()() { fn(); }
-  };
-  size_t counter = 0;
-  auto fnp = std::make_shared<Runnable>();
-  fnp->fn = [&]{ counter++; };
-  x.addPtr(fnp);
-  EXPECT_EQ(counter, 1);
-}
diff --git a/folly/wangle/test/FSM.cpp b/folly/wangle/test/FSM.cpp
deleted file mode 100644 (file)
index 80ee536..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <folly/wangle/detail/FSM.h>
-
-using namespace folly::wangle::detail;
-
-enum class State { A, B };
-
-TEST(FSM, example) {
-  FSM<State> fsm(State::A);
-  int count = 0;
-  int unprotectedCount = 0;
-
-  // somebody set up us the switch
-  auto tryTransition = [&]{
-    switch (fsm.getState()) {
-    case State::A:
-      return fsm.updateState(State::A, State::B, [&]{ count++; });
-    case State::B:
-      return fsm.updateState(State::B, State::A,
-                             [&]{ count--; }, [&]{ unprotectedCount--; });
-    }
-    return false; // unreachable
-  };
-
-  // keep retrying until success (like a cas)
-  while (!tryTransition()) ;
-  EXPECT_EQ(State::B, fsm.getState());
-  EXPECT_EQ(1, count);
-  EXPECT_EQ(0, unprotectedCount);
-
-  while (!tryTransition()) ;
-  EXPECT_EQ(State::A, fsm.getState());
-  EXPECT_EQ(0, count);
-  EXPECT_EQ(-1, unprotectedCount);
-}
-
-TEST(FSM, magicMacrosExample) {
-  struct MyFSM : public FSM<State> {
-    int count = 0;
-    int unprotectedCount = 0;
-    MyFSM() : FSM<State>(State::A) {}
-    void twiddle() {
-      FSM_START
-        FSM_CASE(State::A, State::B, [&]{ count++; });
-        FSM_CASE2(State::B, State::A,
-                  [&]{ count--; }, [&]{ unprotectedCount--; });
-      FSM_END
-    }
-  };
-
-  MyFSM fsm;
-
-  fsm.twiddle();
-  EXPECT_EQ(State::B, fsm.getState());
-  EXPECT_EQ(1, fsm.count);
-  EXPECT_EQ(0, fsm.unprotectedCount);
-
-  fsm.twiddle();
-  EXPECT_EQ(State::A, fsm.getState());
-  EXPECT_EQ(0, fsm.count);
-  EXPECT_EQ(-1, fsm.unprotectedCount);
-}
-
-
-TEST(FSM, ctor) {
-  FSM<State> fsm(State::A);
-  EXPECT_EQ(State::A, fsm.getState());
-}
-
-TEST(FSM, update) {
-  FSM<State> fsm(State::A);
-  EXPECT_TRUE(fsm.updateState(State::A, State::B, []{}));
-  EXPECT_EQ(State::B, fsm.getState());
-}
-
-TEST(FSM, badUpdate) {
-  FSM<State> fsm(State::A);
-  EXPECT_FALSE(fsm.updateState(State::B, State::A, []{}));
-}
-
-TEST(FSM, actionOnUpdate) {
-  FSM<State> fsm(State::A);
-  int count = 0;
-  fsm.updateState(State::A, State::B, [&]{ count++; });
-  EXPECT_EQ(1, count);
-}
-
-TEST(FSM, noActionOnBadUpdate) {
-  FSM<State> fsm(State::A);
-  int count = 0;
-  fsm.updateState(State::B, State::A, [&]{ count++; });
-  EXPECT_EQ(0, count);
-}
-
-TEST(FSM, stateTransitionAfterAction) {
-  FSM<State> fsm(State::A);
-  fsm.updateState(State::A, State::B,
-                  [&]{ EXPECT_EQ(State::A, fsm.getState()); });
-}
diff --git a/folly/wangle/test/FutureTest.cpp b/folly/wangle/test/FutureTest.cpp
deleted file mode 100644 (file)
index d6cfbfb..0000000
+++ /dev/null
@@ -1,1014 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <atomic>
-#include <folly/small_vector.h>
-#include <gtest/gtest.h>
-#include <memory>
-#include <string>
-#include <thread>
-#include <type_traits>
-#include <unistd.h>
-#include <folly/Memory.h>
-#include <folly/Executor.h>
-#include <folly/wangle/Future.h>
-#include <folly/wangle/ManualExecutor.h>
-#include <folly/MPMCQueue.h>
-
-#include <folly/io/async/Request.h>
-
-using namespace folly;
-using namespace folly::wangle;
-using std::pair;
-using std::string;
-using std::unique_ptr;
-using std::vector;
-
-#define EXPECT_TYPE(x, T) \
-  EXPECT_TRUE((std::is_same<decltype(x), T>::value))
-
-/// Simple executor that does work in another thread
-class ThreadExecutor : public Executor {
-  folly::MPMCQueue<Func> funcs;
-  std::atomic<bool> done {false};
-  std::thread worker;
-  folly::Baton<> baton;
-
-  void work() {
-    baton.post();
-    Func fn;
-    while (!done) {
-      while (!funcs.isEmpty()) {
-        funcs.blockingRead(fn);
-        fn();
-      }
-    }
-  }
-
- public:
-  ThreadExecutor(size_t n = 1024)
-    : funcs(n), worker(std::bind(&ThreadExecutor::work, this)) {}
-
-  ~ThreadExecutor() {
-    done = true;
-    funcs.write([]{});
-    worker.join();
-  }
-
-  void add(Func fn) override {
-    funcs.blockingWrite(std::move(fn));
-  }
-
-  void waitForStartup() {
-    baton.wait();
-  }
-};
-
-typedef WangleException eggs_t;
-static eggs_t eggs("eggs");
-
-// Future
-
-TEST(Future, try) {
-  class A {
-   public:
-    A(int x) : x_(x) {}
-
-    int x() const {
-      return x_;
-    }
-   private:
-    int x_;
-  };
-
-  A a(5);
-  Try<A> t_a(std::move(a));
-
-  Try<void> t_void;
-
-  EXPECT_EQ(5, t_a.value().x());
-}
-
-TEST(Future, special) {
-  EXPECT_FALSE(std::is_copy_constructible<Future<int>>::value);
-  EXPECT_FALSE(std::is_copy_assignable<Future<int>>::value);
-  EXPECT_TRUE(std::is_move_constructible<Future<int>>::value);
-  EXPECT_TRUE(std::is_move_assignable<Future<int>>::value);
-}
-
-TEST(Future, thenTry) {
-  bool flag = false;
-
-  makeFuture<int>(42).then([&](Try<int>&& t) {
-                              flag = true;
-                              EXPECT_EQ(42, t.value());
-                            });
-  EXPECT_TRUE(flag); flag = false;
-
-  makeFuture<int>(42)
-    .then([](Try<int>&& t) { return t.value(); })
-    .then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); });
-  EXPECT_TRUE(flag); flag = false;
-
-  makeFuture().then([&](Try<void>&& t) { flag = true; t.value(); });
-  EXPECT_TRUE(flag); flag = false;
-
-  Promise<void> p;
-  auto f = p.getFuture().then([&](Try<void>&& t) { flag = true; });
-  EXPECT_FALSE(flag);
-  EXPECT_FALSE(f.isReady());
-  p.setValue();
-  EXPECT_TRUE(flag);
-  EXPECT_TRUE(f.isReady());
-}
-
-TEST(Future, thenValue) {
-  bool flag = false;
-  makeFuture<int>(42).then([&](int i){
-    EXPECT_EQ(42, i);
-    flag = true;
-  });
-  EXPECT_TRUE(flag); flag = false;
-
-  makeFuture<int>(42)
-    .then([](int i){ return i; })
-    .then([&](int i) { flag = true; EXPECT_EQ(42, i); });
-  EXPECT_TRUE(flag); flag = false;
-
-  makeFuture().then([&]{
-    flag = true;
-  });
-  EXPECT_TRUE(flag); flag = false;
-
-  auto f = makeFuture<int>(eggs).then([&](int i){});
-  EXPECT_THROW(f.value(), eggs_t);
-
-  f = makeFuture<void>(eggs).then([&]{});
-  EXPECT_THROW(f.value(), eggs_t);
-}
-
-TEST(Future, thenValueFuture) {
-  bool flag = false;
-  makeFuture<int>(42)
-    .then([](int i){ return makeFuture<int>(std::move(i)); })
-    .then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); });
-  EXPECT_TRUE(flag); flag = false;
-
-  makeFuture()
-    .then([]{ return makeFuture(); })
-    .then([&](Try<void>&& t) { flag = true; });
-  EXPECT_TRUE(flag); flag = false;
-}
-
-static string doWorkStatic(Try<string>&& t) {
-  return t.value() + ";static";
-}
-
-TEST(Future, thenFunction) {
-  struct Worker {
-    string doWork(Try<string>&& t) {
-      return t.value() + ";class";
-    }
-    static string doWorkStatic(Try<string>&& t) {
-      return t.value() + ";class-static";
-    }
-  } w;
-
-  auto f = makeFuture<string>("start")
-    .then(doWorkStatic)
-    .then(Worker::doWorkStatic)
-    .then(&w, &Worker::doWork);
-
-  EXPECT_EQ(f.value(), "start;static;class-static;class");
-}
-
-static Future<string> doWorkStaticFuture(Try<string>&& t) {
-  return makeFuture(t.value() + ";static");
-}
-
-TEST(Future, thenFunctionFuture) {
-  struct Worker {
-    Future<string> doWorkFuture(Try<string>&& t) {
-      return makeFuture(t.value() + ";class");
-    }
-    static Future<string> doWorkStaticFuture(Try<string>&& t) {
-      return makeFuture(t.value() + ";class-static");
-    }
-  } w;
-
-  auto f = makeFuture<string>("start")
-    .then(doWorkStaticFuture)
-    .then(Worker::doWorkStaticFuture)
-    .then(&w, &Worker::doWorkFuture);
-
-  EXPECT_EQ(f.value(), "start;static;class-static;class");
-}
-
-TEST(Future, value) {
-  auto f = makeFuture(unique_ptr<int>(new int(42)));
-  auto up = std::move(f.value());
-  EXPECT_EQ(42, *up);
-
-  EXPECT_THROW(makeFuture<int>(eggs).value(), eggs_t);
-}
-
-TEST(Future, isReady) {
-  Promise<int> p;
-  auto f = p.getFuture();
-  EXPECT_FALSE(f.isReady());
-  p.setValue(42);
-  EXPECT_TRUE(f.isReady());
-  }
-
-TEST(Future, futureNotReady) {
-  Promise<int> p;
-  Future<int> f = p.getFuture();
-  EXPECT_THROW(f.value(), eggs_t);
-}
-
-TEST(Future, hasException) {
-  EXPECT_TRUE(makeFuture<int>(eggs).getTry().hasException());
-  EXPECT_FALSE(makeFuture(42).getTry().hasException());
-}
-
-TEST(Future, hasValue) {
-  EXPECT_TRUE(makeFuture(42).getTry().hasValue());
-  EXPECT_FALSE(makeFuture<int>(eggs).getTry().hasValue());
-}
-
-TEST(Future, makeFuture) {
-  EXPECT_TYPE(makeFuture(42), Future<int>);
-  EXPECT_EQ(42, makeFuture(42).value());
-
-  EXPECT_TYPE(makeFuture<float>(42), Future<float>);
-  EXPECT_EQ(42, makeFuture<float>(42).value());
-
-  auto fun = [] { return 42; };
-  EXPECT_TYPE(makeFutureTry(fun), Future<int>);
-  EXPECT_EQ(42, makeFutureTry(fun).value());
-
-  auto failfun = []() -> int { throw eggs; };
-  EXPECT_TYPE(makeFutureTry(failfun), Future<int>);
-  EXPECT_THROW(makeFutureTry(failfun).value(), eggs_t);
-
-  EXPECT_TYPE(makeFuture(), Future<void>);
-}
-
-// Promise
-
-TEST(Promise, special) {
-  EXPECT_FALSE(std::is_copy_constructible<Promise<int>>::value);
-  EXPECT_FALSE(std::is_copy_assignable<Promise<int>>::value);
-  EXPECT_TRUE(std::is_move_constructible<Promise<int>>::value);
-  EXPECT_TRUE(std::is_move_assignable<Promise<int>>::value);
-}
-
-TEST(Promise, getFuture) {
-  Promise<int> p;
-  Future<int> f = p.getFuture();
-  EXPECT_FALSE(f.isReady());
-}
-
-TEST(Promise, setValue) {
-  Promise<int> fund;
-  auto ffund = fund.getFuture();
-  fund.setValue(42);
-  EXPECT_EQ(42, ffund.value());
-
-  struct Foo {
-    string name;
-    int value;
-  };
-
-  Promise<Foo> pod;
-  auto fpod = pod.getFuture();
-  Foo f = {"the answer", 42};
-  pod.setValue(f);
-  Foo f2 = fpod.value();
-  EXPECT_EQ(f.name, f2.name);
-  EXPECT_EQ(f.value, f2.value);
-
-  pod = Promise<Foo>();
-  fpod = pod.getFuture();
-  pod.setValue(std::move(f2));
-  Foo f3 = fpod.value();
-  EXPECT_EQ(f.name, f3.name);
-  EXPECT_EQ(f.value, f3.value);
-
-  Promise<unique_ptr<int>> mov;
-  auto fmov = mov.getFuture();
-  mov.setValue(unique_ptr<int>(new int(42)));
-  unique_ptr<int> ptr = std::move(fmov.value());
-  EXPECT_EQ(42, *ptr);
-
-  Promise<void> v;
-  auto fv = v.getFuture();
-  v.setValue();
-  EXPECT_TRUE(fv.isReady());
-}
-
-TEST(Promise, setException) {
-  {
-    Promise<void> p;
-    auto f = p.getFuture();
-    p.setException(eggs);
-    EXPECT_THROW(f.value(), eggs_t);
-  }
-  {
-    Promise<void> p;
-    auto f = p.getFuture();
-    try {
-      throw eggs;
-    } catch (...) {
-      p.setException(std::current_exception());
-    }
-    EXPECT_THROW(f.value(), eggs_t);
-  }
-}
-
-TEST(Promise, fulfil) {
-  {
-    Promise<int> p;
-    auto f = p.getFuture();
-    p.fulfil([] { return 42; });
-    EXPECT_EQ(42, f.value());
-  }
-  {
-    Promise<int> p;
-    auto f = p.getFuture();
-    p.fulfil([]() -> int { throw eggs; });
-    EXPECT_THROW(f.value(), eggs_t);
-  }
-}
-
-TEST(Future, finish) {
-  auto x = std::make_shared<int>(0);
-  {
-    Promise<int> p;
-    auto f = p.getFuture().then([x](Try<int>&& t) { *x = t.value(); });
-
-    // The callback hasn't executed
-    EXPECT_EQ(0, *x);
-
-    // The callback has a reference to x
-    EXPECT_EQ(2, x.use_count());
-
-    p.setValue(42);
-
-    // the callback has executed
-    EXPECT_EQ(42, *x);
-  }
-  // the callback has been destructed
-  // and has released its reference to x
-  EXPECT_EQ(1, x.use_count());
-}
-
-TEST(Future, unwrap) {
-  Promise<int> a;
-  Promise<int> b;
-
-  auto fa = a.getFuture();
-  auto fb = b.getFuture();
-
-  bool flag1 = false;
-  bool flag2 = false;
-
-  // do a, then do b, and get the result of a + b.
-  Future<int> f = fa.then([&](Try<int>&& ta) {
-    auto va = ta.value();
-    flag1 = true;
-    return fb.then([va, &flag2](Try<int>&& tb) {
-      flag2 = true;
-      return va + tb.value();
-    });
-  });
-
-  EXPECT_FALSE(flag1);
-  EXPECT_FALSE(flag2);
-  EXPECT_FALSE(f.isReady());
-
-  a.setValue(3);
-  EXPECT_TRUE(flag1);
-  EXPECT_FALSE(flag2);
-  EXPECT_FALSE(f.isReady());
-
-  b.setValue(4);
-  EXPECT_TRUE(flag1);
-  EXPECT_TRUE(flag2);
-  EXPECT_EQ(7, f.value());
-}
-
-TEST(Future, whenAll) {
-  // returns a vector variant
-  {
-    vector<Promise<int>> promises(10);
-    vector<Future<int>> futures;
-
-    for (auto& p : promises)
-      futures.push_back(p.getFuture());
-
-    auto allf = whenAll(futures.begin(), futures.end());
-
-    random_shuffle(promises.begin(), promises.end());
-    for (auto& p : promises) {
-      EXPECT_FALSE(allf.isReady());
-      p.setValue(42);
-    }
-
-    EXPECT_TRUE(allf.isReady());
-    auto& results = allf.value();
-    for (auto& t : results) {
-      EXPECT_EQ(42, t.value());
-    }
-  }
-
-  // check error semantics
-  {
-    vector<Promise<int>> promises(4);
-    vector<Future<int>> futures;
-
-    for (auto& p : promises)
-      futures.push_back(p.getFuture());
-
-    auto allf = whenAll(futures.begin(), futures.end());
-
-
-    promises[0].setValue(42);
-    promises[1].setException(eggs);
-
-    EXPECT_FALSE(allf.isReady());
-
-    promises[2].setValue(42);
-
-    EXPECT_FALSE(allf.isReady());
-
-    promises[3].setException(eggs);
-
-    EXPECT_TRUE(allf.isReady());
-    EXPECT_FALSE(allf.getTry().hasException());
-
-    auto& results = allf.value();
-    EXPECT_EQ(42, results[0].value());
-    EXPECT_TRUE(results[1].hasException());
-    EXPECT_EQ(42, results[2].value());
-    EXPECT_TRUE(results[3].hasException());
-  }
-
-  // check that futures are ready in then()
-  {
-    vector<Promise<void>> promises(10);
-    vector<Future<void>> futures;
-
-    for (auto& p : promises)
-      futures.push_back(p.getFuture());
-
-    auto allf = whenAll(futures.begin(), futures.end())
-      .then([](Try<vector<Try<void>>>&& ts) {
-        for (auto& f : ts.value())
-          f.value();
-      });
-
-    random_shuffle(promises.begin(), promises.end());
-    for (auto& p : promises)
-      p.setValue();
-    EXPECT_TRUE(allf.isReady());
-  }
-}
-
-
-TEST(Future, whenAny) {
-  {
-    vector<Promise<int>> promises(10);
-    vector<Future<int>> futures;
-
-    for (auto& p : promises)
-      futures.push_back(p.getFuture());
-
-    for (auto& f : futures) {
-      EXPECT_FALSE(f.isReady());
-    }
-
-    auto anyf = whenAny(futures.begin(), futures.end());
-
-    /* futures were moved in, so these are invalid now */
-    EXPECT_FALSE(anyf.isReady());
-
-    promises[7].setValue(42);
-    EXPECT_TRUE(anyf.isReady());
-    auto& idx_fut = anyf.value();
-
-    auto i = idx_fut.first;
-    EXPECT_EQ(7, i);
-
-    auto& f = idx_fut.second;
-    EXPECT_EQ(42, f.value());
-  }
-
-  // error
-  {
-    vector<Promise<void>> promises(10);
-    vector<Future<void>> futures;
-
-    for (auto& p : promises)
-      futures.push_back(p.getFuture());
-
-    for (auto& f : futures) {
-      EXPECT_FALSE(f.isReady());
-    }
-
-    auto anyf = whenAny(futures.begin(), futures.end());
-
-    EXPECT_FALSE(anyf.isReady());
-
-    promises[3].setException(eggs);
-    EXPECT_TRUE(anyf.isReady());
-    EXPECT_TRUE(anyf.value().second.hasException());
-  }
-
-  // then()
-  {
-    vector<Promise<int>> promises(10);
-    vector<Future<int>> futures;
-
-    for (auto& p : promises)
-      futures.push_back(p.getFuture());
-
-    auto anyf = whenAny(futures.begin(), futures.end())
-      .then([](Try<pair<size_t, Try<int>>>&& f) {
-        EXPECT_EQ(42, f.value().second.value());
-      });
-
-    promises[3].setValue(42);
-    EXPECT_TRUE(anyf.isReady());
-  }
-}
-
-
-TEST(when, already_completed) {
-  {
-    vector<Future<void>> fs;
-    for (int i = 0; i < 10; i++)
-      fs.push_back(makeFuture());
-
-    whenAll(fs.begin(), fs.end())
-      .then([&](Try<vector<Try<void>>>&& t) {
-        EXPECT_EQ(fs.size(), t.value().size());
-      });
-  }
-  {
-    vector<Future<int>> fs;
-    for (int i = 0; i < 10; i++)
-      fs.push_back(makeFuture(i));
-
-    whenAny(fs.begin(), fs.end())
-      .then([&](Try<pair<size_t, Try<int>>>&& t) {
-        auto& p = t.value();
-        EXPECT_EQ(p.first, p.second.value());
-      });
-  }
-}
-
-TEST(when, whenN) {
-  vector<Promise<void>> promises(10);
-  vector<Future<void>> futures;
-
-  for (auto& p : promises)
-    futures.push_back(p.getFuture());
-
-  bool flag = false;
-  size_t n = 3;
-  whenN(futures.begin(), futures.end(), n)
-    .then([&](Try<vector<pair<size_t, Try<void>>>>&& t) {
-      flag = true;
-      auto v = t.value();
-      EXPECT_EQ(n, v.size());
-      for (auto& tt : v)
-        EXPECT_TRUE(tt.second.hasValue());
-    });
-
-  promises[0].setValue();
-  EXPECT_FALSE(flag);
-  promises[1].setValue();
-  EXPECT_FALSE(flag);
-  promises[2].setValue();
-  EXPECT_TRUE(flag);
-}
-
-/* Ensure that we can compile when_{all,any} with folly::small_vector */
-TEST(when, small_vector) {
-
-  static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future<void>),
-                "Futures should not be trivially copyable");
-  static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future<int>),
-                "Futures should not be trivially copyable");
-
-  using folly::small_vector;
-  {
-    small_vector<Future<void>> futures;
-
-    for (int i = 0; i < 10; i++)
-      futures.push_back(makeFuture());
-
-    auto anyf = whenAny(futures.begin(), futures.end());
-  }
-
-  {
-    small_vector<Future<void>> futures;
-
-    for (int i = 0; i < 10; i++)
-      futures.push_back(makeFuture());
-
-    auto allf = whenAll(futures.begin(), futures.end());
-  }
-}
-
-TEST(Future, whenAllVariadic) {
-  Promise<bool> pb;
-  Promise<int> pi;
-  Future<bool> fb = pb.getFuture();
-  Future<int> fi = pi.getFuture();
-  bool flag = false;
-  whenAll(std::move(fb), std::move(fi))
-    .then([&](Try<std::tuple<Try<bool>, Try<int>>>&& t) {
-      flag = true;
-      EXPECT_TRUE(t.hasValue());
-      EXPECT_TRUE(std::get<0>(t.value()).hasValue());
-      EXPECT_EQ(std::get<0>(t.value()).value(), true);
-      EXPECT_TRUE(std::get<1>(t.value()).hasValue());
-      EXPECT_EQ(std::get<1>(t.value()).value(), 42);
-    });
-  pb.setValue(true);
-  EXPECT_FALSE(flag);
-  pi.setValue(42);
-  EXPECT_TRUE(flag);
-}
-
-TEST(Future, whenAllVariadicReferences) {
-  Promise<bool> pb;
-  Promise<int> pi;
-  Future<bool> fb = pb.getFuture();
-  Future<int> fi = pi.getFuture();
-  bool flag = false;
-  whenAll(fb, fi)
-    .then([&](Try<std::tuple<Try<bool>, Try<int>>>&& t) {
-      flag = true;
-      EXPECT_TRUE(t.hasValue());
-      EXPECT_TRUE(std::get<0>(t.value()).hasValue());
-      EXPECT_EQ(std::get<0>(t.value()).value(), true);
-      EXPECT_TRUE(std::get<1>(t.value()).hasValue());
-      EXPECT_EQ(std::get<1>(t.value()).value(), 42);
-    });
-  pb.setValue(true);
-  EXPECT_FALSE(flag);
-  pi.setValue(42);
-  EXPECT_TRUE(flag);
-}
-
-TEST(Future, whenAll_none) {
-  vector<Future<int>> fs;
-  auto f = whenAll(fs.begin(), fs.end());
-  EXPECT_TRUE(f.isReady());
-}
-
-TEST(Future, throwCaughtInImmediateThen) {
-  // Neither of these should throw "Promise already satisfied"
-  makeFuture().then(
-    [=](Try<void>&&) -> int { throw std::exception(); });
-  makeFuture().then(
-    [=](Try<void>&&) -> Future<int> { throw std::exception(); });
-}
-
-TEST(Future, throwIfFailed) {
-  makeFuture<void>(eggs)
-    .then([=](Try<void>&& t) {
-      EXPECT_THROW(t.throwIfFailed(), eggs_t);
-    });
-  makeFuture()
-    .then([=](Try<void>&& t) {
-      EXPECT_NO_THROW(t.throwIfFailed());
-    });
-
-  makeFuture<int>(eggs)
-    .then([=](Try<int>&& t) {
-      EXPECT_THROW(t.throwIfFailed(), eggs_t);
-    });
-  makeFuture<int>(42)
-    .then([=](Try<int>&& t) {
-      EXPECT_NO_THROW(t.throwIfFailed());
-    });
-}
-
-TEST(Future, waitWithSemaphoreImmediate) {
-  waitWithSemaphore(makeFuture());
-  auto done = waitWithSemaphore(makeFuture(42)).value();
-  EXPECT_EQ(42, done);
-
-  vector<int> v{1,2,3};
-  auto done_v = waitWithSemaphore(makeFuture(v)).value();
-  EXPECT_EQ(v.size(), done_v.size());
-  EXPECT_EQ(v, done_v);
-
-  vector<Future<void>> v_f;
-  v_f.push_back(makeFuture());
-  v_f.push_back(makeFuture());
-  auto done_v_f = waitWithSemaphore(whenAll(v_f.begin(), v_f.end())).value();
-  EXPECT_EQ(2, done_v_f.size());
-
-  vector<Future<bool>> v_fb;
-  v_fb.push_back(makeFuture(true));
-  v_fb.push_back(makeFuture(false));
-  auto fut = whenAll(v_fb.begin(), v_fb.end());
-  auto done_v_fb = std::move(waitWithSemaphore(std::move(fut)).value());
-  EXPECT_EQ(2, done_v_fb.size());
-}
-
-TEST(Future, waitWithSemaphore) {
-  Promise<int> p;
-  Future<int> f = p.getFuture();
-  std::atomic<bool> flag{false};
-  std::atomic<int> result{1};
-  std::atomic<std::thread::id> id;
-
-  std::thread t([&](Future<int>&& tf){
-      auto n = tf.then([&](Try<int> && t) {
-          id = std::this_thread::get_id();
-          return t.value();
-        });
-      flag = true;
-      result.store(waitWithSemaphore(std::move(n)).value());
-    },
-    std::move(f)
-    );
-  while(!flag){}
-  EXPECT_EQ(result.load(), 1);
-  p.setValue(42);
-  t.join();
-  // validate that the callback ended up executing in this thread, which
-  // is more to ensure that this test actually tests what it should
-  EXPECT_EQ(id, std::this_thread::get_id());
-  EXPECT_EQ(result.load(), 42);
-}
-
-TEST(Future, waitWithSemaphoreForTime) {
- {
-  Promise<int> p;
-  Future<int> f = p.getFuture();
-  auto t = waitWithSemaphore(std::move(f),
-    std::chrono::microseconds(1));
-  EXPECT_FALSE(t.isReady());
-  p.setValue(1);
-  EXPECT_TRUE(t.isReady());
- }
- {
-  Promise<int> p;
-  Future<int> f = p.getFuture();
-  p.setValue(1);
-  auto t = waitWithSemaphore(std::move(f),
-    std::chrono::milliseconds(1));
-  EXPECT_TRUE(t.isReady());
- }
- {
-  vector<Future<bool>> v_fb;
-  v_fb.push_back(makeFuture(true));
-  v_fb.push_back(makeFuture(false));
-  auto f = whenAll(v_fb.begin(), v_fb.end());
-  auto t = waitWithSemaphore(std::move(f),
-    std::chrono::milliseconds(1));
-  EXPECT_TRUE(t.isReady());
-  EXPECT_EQ(2, t.value().size());
- }
- {
-  vector<Future<bool>> v_fb;
-  Promise<bool> p1;
-  Promise<bool> p2;
-  v_fb.push_back(p1.getFuture());
-  v_fb.push_back(p2.getFuture());
-  auto f = whenAll(v_fb.begin(), v_fb.end());
-  auto t = waitWithSemaphore(std::move(f),
-    std::chrono::milliseconds(1));
-  EXPECT_FALSE(t.isReady());
-  p1.setValue(true);
-  EXPECT_FALSE(t.isReady());
-  p2.setValue(true);
-  EXPECT_TRUE(t.isReady());
- }
- {
-  auto t = waitWithSemaphore(makeFuture(),
-    std::chrono::milliseconds(1));
-  EXPECT_TRUE(t.isReady());
- }
-}
-
-TEST(Future, callbackAfterActivate) {
-  Promise<void> p;
-  auto f = p.getFuture();
-  f.deactivate();
-
-  size_t count = 0;
-  f.then([&](Try<void>&&) { count++; });
-
-  p.setValue();
-  EXPECT_EQ(0, count);
-
-  f.activate();
-  EXPECT_EQ(1, count);
-}
-
-TEST(Future, activateOnDestruct) {
-  auto f = std::make_shared<Future<void>>(makeFuture());
-  f->deactivate();
-
-  size_t count = 0;
-  f->then([&](Try<void>&&) { count++; });
-  EXPECT_EQ(0, count);
-
-  f.reset();
-  EXPECT_EQ(1, count);
-}
-
-TEST(Future, viaActsCold) {
-  ManualExecutor x;
-  size_t count = 0;
-
-  auto fv = via(&x);
-  fv.then([&](Try<void>&&) { count++; });
-
-  EXPECT_EQ(0, count);
-
-  fv.activate();
-
-  EXPECT_EQ(1, x.run());
-  EXPECT_EQ(1, count);
-}
-
-TEST(Future, viaIsCold) {
-  ManualExecutor x;
-  EXPECT_FALSE(via(&x).isActive());
-}
-
-TEST(Future, viaRaces) {
-  ManualExecutor x;
-  Promise<void> p;
-  auto tid = std::this_thread::get_id();
-  bool done = false;
-
-  std::thread t1([&] {
-    p.getFuture()
-      .via(&x)
-      .then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
-      .then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
-      .then([&](Try<void>&&) { done = true; });
-  });
-
-  std::thread t2([&] {
-    p.setValue();
-  });
-
-  while (!done) x.run();
-  t1.join();
-  t2.join();
-}
-
-// TODO(#4920689)
-TEST(Future, viaRaces_2stage) {
-  ManualExecutor x;
-  Promise<void> p;
-  auto tid = std::this_thread::get_id();
-  bool done = false;
-
-  std::thread t1([&] {
-    auto f2 = p.getFuture().via(&x);
-    f2.then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
-      .then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
-      .then([&](Try<void>&&) { done = true; });
-
-    // the bug was in the promise being fulfilled before f2 is reactivated. we
-    // could sleep, but yielding should cause this to fail with reasonable
-    // probability
-    std::this_thread::yield();
-    f2.activate();
-  });
-
-  std::thread t2([&] {
-    p.setValue();
-  });
-
-  while (!done) x.run();
-  t1.join();
-  t2.join();
-}
-
-TEST(Future, getFuture_after_setValue) {
-  Promise<int> p;
-  p.setValue(42);
-  EXPECT_EQ(42, p.getFuture().value());
-}
-
-TEST(Future, getFuture_after_setException) {
-  Promise<void> p;
-  p.fulfil([]() -> void { throw std::logic_error("foo"); });
-  EXPECT_THROW(p.getFuture().value(), std::logic_error);
-}
-
-TEST(Future, detachRace) {
-  // Task #5438209
-  // This test is designed to detect a race that was in Core::detachOne()
-  // where detached_ was incremented and then tested, and that
-  // allowed a race where both Promise and Future would think they were the
-  // second and both try to delete. This showed up at scale but was very
-  // difficult to reliably repro in a test. As it is, this only fails about
-  // once in every 1,000 executions. Doing this 1,000 times is going to make a
-  // slow test so I won't do that but if it ever fails, take it seriously, and
-  // run the test binary with "--gtest_repeat=10000 --gtest_filter=*detachRace"
-  // (Don't forget to enable ASAN)
-  auto p = folly::make_unique<Promise<bool>>();
-  auto f = folly::make_unique<Future<bool>>(p->getFuture());
-  folly::Baton<> baton;
-  std::thread t1([&]{
-    baton.post();
-    p.reset();
-  });
-  baton.wait();
-  f.reset();
-  t1.join();
-}
-
-class TestData : public RequestData {
- public:
-  explicit TestData(int data) : data_(data) {}
-  virtual ~TestData() {}
-  int data_;
-};
-
-TEST(Future, context) {
-
-  // Start a new context
-  RequestContext::create();
-
-  EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test"));
-
-  // Set some test data
-  RequestContext::get()->setContextData(
-    "test",
-    std::unique_ptr<TestData>(new TestData(10)));
-
-  // Start a future
-  Promise<void> p;
-  auto future = p.getFuture().then([&]{
-    // Check that the context followed the future
-    EXPECT_TRUE(RequestContext::get() != nullptr);
-    auto a = dynamic_cast<TestData*>(
-      RequestContext::get()->getContextData("test"));
-    auto data = a->data_;
-    EXPECT_EQ(10, data);
-  });
-
-  // Clear the context
-  RequestContext::setContext(nullptr);
-
-  EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test"));
-
-  // Fulfil the promise
-  p.setValue();
-}
-
-
-// This only fails about 1 in 1k times when the bug is present :(
-TEST(Future, t5506504) {
-  ThreadExecutor x;
-
-  auto fn = [&x]{
-    auto promises = std::make_shared<vector<Promise<void>>>(4);
-    vector<Future<void>> futures;
-
-    for (auto& p : *promises) {
-      futures.emplace_back(
-        p.getFuture()
-        .via(&x)
-        .then([](Try<void>&&){}));
-    }
-
-    x.waitForStartup();
-    x.add([promises]{
-      for (auto& p : *promises) p.setValue();
-    });
-
-    return whenAll(futures.begin(), futures.end());
-  };
-
-  waitWithSemaphore(fn());
-}
diff --git a/folly/wangle/test/Interrupts.cpp b/folly/wangle/test/Interrupts.cpp
deleted file mode 100644 (file)
index 41d5bf7..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <folly/wangle/Future.h>
-#include <folly/wangle/Promise.h>
-
-using namespace folly::wangle;
-
-TEST(Interrupts, raise) {
-  std::runtime_error eggs("eggs");
-  Promise<void> p;
-  p.setInterruptHandler([&](std::exception_ptr e) {
-    EXPECT_THROW(std::rethrow_exception(e), decltype(eggs));
-  });
-  p.getFuture().raise(eggs);
-}
-
-TEST(Interrupts, cancel) {
-  Promise<void> p;
-  p.setInterruptHandler([&](std::exception_ptr e) {
-    EXPECT_THROW(std::rethrow_exception(e), FutureCancellation);
-  });
-  p.getFuture().cancel();
-}
-
-TEST(Interrupts, handleThenInterrupt) {
-  Promise<int> p;
-  bool flag = false;
-  p.setInterruptHandler([&](std::exception_ptr e) { flag = true; });
-  p.getFuture().cancel();
-  EXPECT_TRUE(flag);
-}
-
-TEST(Interrupts, interruptThenHandle) {
-  Promise<int> p;
-  bool flag = false;
-  p.getFuture().cancel();
-  p.setInterruptHandler([&](std::exception_ptr e) { flag = true; });
-  EXPECT_TRUE(flag);
-}
-
-TEST(Interrupts, interruptAfterFulfilNoop) {
-  Promise<void> p;
-  bool flag = false;
-  p.setInterruptHandler([&](std::exception_ptr e) { flag = true; });
-  p.setValue();
-  p.getFuture().cancel();
-  EXPECT_FALSE(flag);
-}
-
-TEST(Interrupts, secondInterruptNoop) {
-  Promise<void> p;
-  int count = 0;
-  p.setInterruptHandler([&](std::exception_ptr e) { count++; });
-  auto f = p.getFuture();
-  f.cancel();
-  f.cancel();
-  EXPECT_EQ(1, count);
-}
diff --git a/folly/wangle/test/Thens.cpp b/folly/wangle/test/Thens.cpp
deleted file mode 100644 (file)
index e8ac875..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-// This file is @generated by thens.rb. Do not edit directly.
-
-// TODO: fails to compile with clang:dev.  See task #4412111
-#ifndef __clang__
-
-#include <folly/wangle/test/Thens.h>
-
-TEST(Future, thenVariants) {
-  SomeClass anObject;
-  folly::Executor* anExecutor;
-
-  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A>&&>);}
-  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, Try<A>&&>);}
-  {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, Try<A>&&>);}
-  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A>&&>());}
-  {Future<B> f = someFuture<A>().then([&](Try<A>&&){return someFuture<B>();});}
-  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, A&&>);}
-  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, A&&>);}
-  {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<Future<B>, A&&>);}
-  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, A&&>());}
-  {Future<B> f = someFuture<A>().then([&](A&&){return someFuture<B>();});}
-  {Future<B> f = someFuture<A>().then(&aFunction<B, Try<A>&&>);}
-  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A>&&>);}
-  {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, Try<A>&&>);}
-  {Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A>&&>());}
-  {Future<B> f = someFuture<A>().then([&](Try<A>&&){return B();});}
-  {Future<B> f = someFuture<A>().then(&aFunction<B, A&&>);}
-  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, A&&>);}
-  {Future<B> f = someFuture<A>().then(&anObject, &SomeClass::aMethod<B, A&&>);}
-  {Future<B> f = someFuture<A>().then(aStdFunction<B, A&&>());}
-  {Future<B> f = someFuture<A>().then([&](A&&){return B();});}
-}
-
-#endif
diff --git a/folly/wangle/test/Thens.h b/folly/wangle/test/Thens.h
deleted file mode 100644 (file)
index 928f820..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <gtest/gtest.h>
-#include <memory>
-#include <folly/wangle/Future.h>
-#include <folly/Executor.h>
-
-using namespace folly::wangle;
-using namespace std;
-using namespace testing;
-
-typedef unique_ptr<int> A;
-struct B {};
-
-template <class T>
-using EnableIfFuture = typename std::enable_if<isFuture<T>::value>::type;
-
-template <class T>
-using EnableUnlessFuture = typename std::enable_if<!isFuture<T>::value>::type;
-
-template <class T>
-Future<T> someFuture() {
-  return makeFuture(T());
-}
-
-template <class Ret, class... Params, typename = void>
-Ret aFunction(Params...);
-
-template <class Ret, class... Params>
-typename std::enable_if<isFuture<Ret>::value, Ret>::type
-aFunction(Params...) {
-  typedef typename Ret::value_type T;
-  return makeFuture(T());
-}
-
-template <class Ret, class... Params>
-typename std::enable_if<!isFuture<Ret>::value, Ret>::type
-aFunction(Params...) {
-  return Ret();
-}
-
-template <class Ret, class... Params>
-std::function<Ret(Params...)>
-aStdFunction(
-    typename std::enable_if<!isFuture<Ret>::value, bool>::type = false) {
-  return [](Params...) -> Ret { return Ret(); };
-}
-
-template <class Ret, class... Params>
-std::function<Ret(Params...)>
-aStdFunction(typename std::enable_if<isFuture<Ret>::value, bool>::type = true) {
-  typedef typename Ret::value_type T;
-  return [](Params...) -> Future<T> { return makeFuture(T()); };
-}
-
-class SomeClass {
-  B b;
-public:
-  template <class Ret, class... Params>
-  static Ret aStaticMethod(Params...);
-
-  template <class Ret, class... Params>
-  static
-  typename std::enable_if<!isFuture<Ret>::value, Ret>::type
-  aStaticMethod(Params...) {
-    return Ret();
-  }
-
-  template <class Ret, class... Params>
-  static
-  typename std::enable_if<isFuture<Ret>::value, Ret>::type
-  aStaticMethod(Params...) {
-    typedef typename Ret::value_type T;
-    return makeFuture(T());
-  }
-
-  template <class Ret, class... Params>
-  Ret aMethod(Params...);
-
-  template <class Ret, class... Params>
-  typename std::enable_if<!isFuture<Ret>::value, Ret>::type
-  aMethod(Params...) {
-    return Ret();
-  }
-
-  template <class Ret, class... Params>
-  typename std::enable_if<isFuture<Ret>::value, Ret>::type
-  aMethod(Params...) {
-    typedef typename Ret::value_type T;
-    return makeFuture(T());
-  }
-};
diff --git a/folly/wangle/test/Try.cpp b/folly/wangle/test/Try.cpp
deleted file mode 100644 (file)
index a472bed..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <folly/Memory.h>
-#include <folly/wangle/Try.h>
-
-using namespace folly::wangle;
-
-TEST(Try, makeTryFunction) {
-  auto func = []() {
-    return folly::make_unique<int>(1);
-  };
-
-  auto result = makeTryFunction(func);
-  EXPECT_TRUE(result.hasValue());
-  EXPECT_EQ(*result.value(), 1);
-}
-
-TEST(Try, makeTryFunctionThrow) {
-  auto func = []() {
-    throw std::runtime_error("Runtime");
-    return folly::make_unique<int>(1);
-  };
-
-  auto result = makeTryFunction(func);
-  EXPECT_TRUE(result.hasException());
-}
-
-TEST(Try, makeTryFunctionVoid) {
-  auto func = []() {
-    return;
-  };
-
-  auto result = makeTryFunction(func);
-  EXPECT_TRUE(result.hasValue());
-}
-
-TEST(Try, makeTryFunctionVoidThrow) {
-  auto func = []() {
-    throw std::runtime_error("Runtime");
-    return;
-  };
-
-  auto result = makeTryFunction(func);
-  EXPECT_TRUE(result.hasException());
-}
diff --git a/folly/wangle/test/ViaTest.cpp b/folly/wangle/test/ViaTest.cpp
deleted file mode 100644 (file)
index 3e9953e..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <thread>
-
-#include <folly/wangle/Future.h>
-#include <folly/wangle/InlineExecutor.h>
-#include <folly/wangle/ManualExecutor.h>
-
-using namespace folly::wangle;
-
-struct ManualWaiter {
-  explicit ManualWaiter(std::shared_ptr<ManualExecutor> ex) : ex(ex) {}
-
-  void makeProgress() {
-    ex->wait();
-    ex->run();
-  }
-
-  std::shared_ptr<ManualExecutor> ex;
-};
-
-struct ViaFixture : public testing::Test {
-  ViaFixture() :
-    westExecutor(new ManualExecutor),
-    eastExecutor(new ManualExecutor),
-    waiter(new ManualWaiter(westExecutor)),
-    done(false)
-  {
-    t = std::thread([=] {
-      ManualWaiter eastWaiter(eastExecutor);
-      while (!done)
-        eastWaiter.makeProgress();
-      });
-  }
-
-  ~ViaFixture() {
-    done = true;
-    eastExecutor->add([=]() { });
-    t.join();
-  }
-
-  void addAsync(int a, int b, std::function<void(int&&)>&& cob) {
-    eastExecutor->add([=]() {
-      cob(a + b);
-    });
-  }
-
-  std::shared_ptr<ManualExecutor> westExecutor;
-  std::shared_ptr<ManualExecutor> eastExecutor;
-  std::shared_ptr<ManualWaiter> waiter;
-  InlineExecutor inlineExecutor;
-  bool done;
-  std::thread t;
-};
-
-TEST(Via, exception_on_launch) {
-  auto future = makeFuture<int>(std::runtime_error("E"));
-  EXPECT_THROW(future.value(), std::runtime_error);
-}
-
-TEST(Via, then_value) {
-  auto future = makeFuture(std::move(1))
-    .then([](Try<int>&& t) {
-      return t.value() == 1;
-    })
-    ;
-
-  EXPECT_TRUE(future.value());
-}
-
-TEST(Via, then_future) {
-  auto future = makeFuture(1)
-    .then([](Try<int>&& t) {
-      return makeFuture(t.value() == 1);
-    })
-    ;
-  EXPECT_TRUE(future.value());
-}
-
-static Future<std::string> doWorkStatic(Try<std::string>&& t) {
-  return makeFuture(t.value() + ";static");
-}
-
-TEST(Via, then_function) {
-  struct Worker {
-    Future<std::string> doWork(Try<std::string>&& t) {
-      return makeFuture(t.value() + ";class");
-    }
-    static Future<std::string> doWorkStatic(Try<std::string>&& t) {
-      return makeFuture(t.value() + ";class-static");
-    }
-  } w;
-
-  auto f = makeFuture(std::string("start"))
-    .then(doWorkStatic)
-    .then(Worker::doWorkStatic)
-    .then(&w, &Worker::doWork)
-    ;
-
-  EXPECT_EQ(f.value(), "start;static;class-static;class");
-}
-
-TEST_F(ViaFixture, deactivateChain) {
-  bool flag = false;
-  auto f = makeFuture().deactivate();
-  EXPECT_FALSE(f.isActive());
-  auto f2 = f.then([&](Try<void>){ flag = true; });
-  EXPECT_FALSE(flag);
-}
-
-TEST_F(ViaFixture, deactivateActivateChain) {
-  bool flag = false;
-  // you can do this all day long with temporaries.
-  auto f1 = makeFuture().deactivate().activate().deactivate();
-  // Chaining on activate/deactivate requires an rvalue, so you have to move
-  // one of these two ways (if you're not using a temporary).
-  auto f2 = std::move(f1).activate();
-  f2.deactivate();
-  auto f3 = std::move(f2.activate());
-  f3.then([&](Try<void>){ flag = true; });
-  EXPECT_TRUE(flag);
-}
-
-TEST_F(ViaFixture, thread_hops) {
-  auto westThreadId = std::this_thread::get_id();
-  auto f = via(eastExecutor.get()).then([=](Try<void>&& t) {
-    EXPECT_NE(std::this_thread::get_id(), westThreadId);
-    return makeFuture<int>(1);
-  }).via(westExecutor.get()
-  ).then([=](Try<int>&& t) {
-    EXPECT_EQ(std::this_thread::get_id(), westThreadId);
-    return t.value();
-  });
-  while (!f.isReady()) {
-    waiter->makeProgress();
-  }
-  EXPECT_EQ(f.value(), 1);
-}
-
-TEST_F(ViaFixture, chain_vias) {
-  auto westThreadId = std::this_thread::get_id();
-  auto f = via(eastExecutor.get()).then([=](Try<void>&& t) {
-    EXPECT_NE(std::this_thread::get_id(), westThreadId);
-    return makeFuture<int>(1);
-  }).then([=](Try<int>&& t) {
-    int val = t.value();
-    return makeFuture(std::move(val)).via(westExecutor.get())
-      .then([=](Try<int>&& t) mutable {
-        EXPECT_EQ(std::this_thread::get_id(), westThreadId);
-        return t.value();
-      });
-  }).then([=](Try<int>&& t) {
-    EXPECT_EQ(std::this_thread::get_id(), westThreadId);
-    return t.value();
-  });
-
-  while (!f.isReady()) {
-    waiter->makeProgress();
-  }
-  EXPECT_EQ(f.value(), 1);
-}
-
-TEST_F(ViaFixture, bareViaAssignment) {
-  auto f = via(eastExecutor.get());
-}
-TEST_F(ViaFixture, viaAssignment) {
-  // via()&&
-  auto f = makeFuture().via(eastExecutor.get());
-  // via()&
-  auto f2 = f.via(eastExecutor.get());
-}
diff --git a/folly/wangle/test/main.cpp b/folly/wangle/test/main.cpp
deleted file mode 100644 (file)
index 7dbf27d..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-int main(int argc, char** argv) {
-  ::testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
diff --git a/folly/wangle/test/thens.rb b/folly/wangle/test/thens.rb
deleted file mode 100755 (executable)
index 61ca26d..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/usr/bin/env ruby
-
-# ruby thens.rb > Thens.cpp
-
-# An exercise in combinatorics.
-# (ordinary/static function, member function, std::function, lambda)
-# X
-# returns (Future<R>, R)
-# X
-# accepts (Try<T>&&, Try<T> const&, Try<T>, T&&, T const&, T, nothing)
-
-def test(*args)
-  args = args.join(", ")
-  [
-  "{Future<B> f = someFuture<A>().then(#{args});}",
-  #"{Future<B> f = makeFuture(A()).then(#{args}, anExecutor);}",
-  ]
-end
-
-def retval(ret)
-  {
-    "Future<B>" => "someFuture<B>()",
-    "Try<B>" => "Try<B>(B())",
-    "B" => "B()"
-  }[ret]
-end
-
-return_types = [
-  "Future<B>",
-  "B",
-  #"Try<B>",
-]
-param_types = [
-    "Try<A>&&",
-    #"Try<A> const&",
-    #"Try<A>",
-    #"Try<A>&",
-    "A&&",
-    #"A const&",
-    #"A",
-    #"A&",
-    #"",
-  ]
-
-tests = (
-  return_types.map { |ret|
-    param_types.map { |param|
-      both = "#{ret}, #{param}"
-      [
-        ["&aFunction<#{both}>"],
-        ["&SomeClass::aStaticMethod<#{both}>"],
-        # TODO switch these around (std::bind-style)
-        ["&anObject", "&SomeClass::aMethod<#{both}>"],
-        ["aStdFunction<#{both}>()"],
-        ["[&](#{param}){return #{retval(ret)};}"],
-      ]
-    }
-  }.flatten(2) + [
-    #[""],
-  ]
-).map {|a| test(a)}.flatten
-
-print <<EOF
-// This file is #{"@"}generated by thens.rb. Do not edit directly.
-
-// TODO: fails to compile with clang:dev.  See task #4412111
-#ifndef __clang__
-
-#include <folly/wangle/test/Thens.h>
-
-TEST(Future, thenVariants) {
-  SomeClass anObject;
-  folly::Executor* anExecutor;
-
-  #{tests.join("\n  ")}
-}
-
-#endif
-EOF