From dd5fa0122a9240c9c9c17c9d0eba13e8a4e60f6e Mon Sep 17 00:00:00 2001 From: Hans Fugal Date: Thu, 4 Jun 2015 08:26:12 -0700 Subject: [PATCH] merge dex and readme Summary: This is still manual as in the previous DEX.md revision, but I incorporated README.md into dex and redid the manual export. Test Plan: reading_intensifies Reviewed By: yfeldblum@fb.com Subscribers: exa, folly-diffs@, jsedgwick, yfeldblum, chalfant FB internal diff: D2124555 Tasks: 6047407 Signature: t1:2124555:1433370669:91f8bb55c1e6de4dbeccbb85556f54ae49464c7a --- folly/futures/DEX.md | 839 --------------------------- folly/futures/README.md | 1187 ++++++++++++++++++++++++++++++++------- 2 files changed, 994 insertions(+), 1032 deletions(-) delete mode 100644 folly/futures/DEX.md diff --git a/folly/futures/DEX.md b/folly/futures/DEX.md deleted file mode 100644 index 96535b68..00000000 --- a/folly/futures/DEX.md +++ /dev/null @@ -1,839 +0,0 @@ -

Futures

Futures is a framework for expressing asynchronous code in C++ using the Promise/Future pattern.

Overview #

- -

Folly Futures is an async C++ framework inspired by Twitter's Scala Futures implementation (see Future.scala and friends), and loosely builds upon the existing but anemic Futures code found in the C++11 standard (std::future) and boost::future (especially >= 1.53.0).

- -

Let's dive right in with a contrived and synchronous example of Futures.

- -
Promise<int> p;
-Future<int> f = p.getFuture();
-// ...
-p.setValue(42); // or setException(...)
-cout << f.value(); // prints 42
- -

First, we create a Promise object of type int. This object is exactly what it sounds like—a pledge to provide an int (or an exception) at some point in the future.

- -

Next, we extract a Future object from that promise. You can think of futures as handles on promises - they provide a way to access that int when the promise is fulfilled.

- -

Later, when the promise is fulfilled via setValue() or setException(), that int is accessible via the future's value() method. That method will throw if the future contains an exception.

- -

Setting callbacks with then() #

- -

Ok, great, so now you're wondering what these are actually useful for. Let's consider another way to follow up on the result of a Future once its corresponding Promise is fulfilled - callbacks! Here's a snippet that is functionally equivalent to the one above:

- -
Promise<int> p;
-Future<int> f = p.getFuture();
-
-f.then([](int i){
-  cout << i;
-});
-
-p.setValue(42);
- -

That then() method on futures is the real bread and butter of Futures code. It allows you to provide a callback which will be executed when that Future is complete. Note that while we fulfill the promise after setting the callback here, those operations could be swapped. Setting a callback on an already completed future executes the callback immediately.

- -

In this case, the callback takes a value directly. If the Future contained an exception, the callback will be passed over and the exception will be propagated to the resultant Future - more on that in a second. Your callback may also take a Try, which encapsulates either an exception or a value of its templated type.

- -
f.then([](Try<int> const& t){
-  cout << t.value();
-});
- -
NOTE: Do not use Try unless you are actually going to do exception handling in your callback. It is much cleaner and often more performant to take the value directly when you can. If you want to do exception handling, there still might be better options than Try. See Error Handling.
- -

The real power of then() is that it returns a Future of the type that the callback returns and can therefore be chained and nested with ease. Let's consider another example:

- -
Future<string> f2 = f.then([](int i){
-  return folly::to<string>(i);
-});
-
-f2.then([](string s){ /* ... */ });
- -

Here, we convert that int to a string in the callback and return the result, which results in a Future<string> that we can set further callbacks on. I've created a named variable f2 to demonstrate types but don't hesitate to chain futures directly:

- -
auto finalFuture = getSomeFuture()
-  .then(...)
-  .then(...)
-  .then(...);
- -

That's all great, but this code is still synchronous. These constructs truly become useful when you start to chain, nest, and compose asynchronous operations. Let's say you instead have some remote service that converts your integers to strings for you, and that you also have a client with Future interfaces (i.e. interfaces that return Futures). Now let's leverage the fact that then() also allows you to return Future<T> from inside your callbacks as well as just T:

- -
Future<string> f2 = f.then([](int i){
-  return getClient()->future_intToString(i); // returns Future<string>
-});
-
-f2.then([](Try<string> const& s){ // … });
- -

In general, your code will be cleaner if you return T from your callbacks and only switch to returning Future<T> when necessary (i.e. when there is a nested call to a future-returning function).

- -

Futures, Promises, and move semantics #

- -

Futures and Promises are movable but non-copyable. This preserves the invariant of a one-to-one mapping between a Promise and a Future and as a side effect encourages performant code. There is a piece of heap-allocated shared state underlying each promise-future pair—keep this in mind as a bare minimum performance cost.

- -

Synchronously creating and completing futures #

- -

Synchronously entering and exiting the futures paradigm can be useful, especially in tests, so the following utilities are available:

- -
    -
  • Create already-completed futures with makeFuture<T>(), which takes a T&& (or an exception, more info here). If you pass T&& the type is inferred and you don't have to specify it.
  • -
  • Extract a future's T value with Future<T>::get(). This method is blocking, so make sure that either your future is already completed or that another thread will complete the future while the calling thread blocks. get() can also take a timeout - see Timeouts.
  • -
  • Perform a blocking wait on a Future with Future<T>::wait(). This is just like get() but it instead of extracting the value or throwing the exception, wait() returns a new Future with the result of the input Future. Like get(), wait() can also take a timeout - see Timeouts.
  • -
  • getVia() and waitVia(), which are like get() and wait() except that they drive some Executor (say, an EventBase) until the Future is complete. See Testing for more.
  • -
- -
NOTE: makeFuture(), get(), wait(), and friends are especially handy in tests and are documented further in the Testing article.
- -

Overloads of then() #

- -

Above are demonstrations of variants of then() whose callbacks

- -
    -
  • return Future<T> or T
  • -
  • take T const& or Try<T> const& (also possible are T, Try<T>, T&&, and Try<T>&&)
  • -
- -
NOTE: The preferred pattern is—when possible—to take a T const& or Try<T> const&. This guarantees no extra copies or moves are performed, and encourages value semantics which will reduce your concurrency bugs down the road. If you need to move the value out and return a (modified) version, prefer to accept a T or Try<T> instead of T&& or Try<T>&&. The compiler will optimize them the same, but your code will be more readable and potentially more future-proof against some planned features.
- -

The flexibility doesn't end there. There are also overloads so that you can bind global functions, member functions, and static member functions to then():

- -
void globalFunction(Try<int> const& t);
-
-struct Foo {
-  void memberMethod(Try<int> const& t);
-  static void staticMemberMethod(Try<int> const& t);
-};
-Foo foo;
-
-// bind global function
-makeFuture<int>(1).then(globalFunction);
-// bind member method
-makeFuture<int>(2).then(&Foo::memberMethod, &foo);
-// bind static member method
-makeFuture<int>(3).then(&Foo::staticMemberMethod);
- -

A note on Promises #

- -

Generally speaking, the majority of your futures-based code will deal with Futures alone and not Promises - calling Future-returning interfaces, composing callbacks on them, and eventually returning another Future. Promises are most useful when you're wrapping some lower level asynchronous interface so that you can return a Future:

- -
void fooOldFashioned(int arg, std::function<int(int)> callback);
-
-Future<int> foo(int arg) {
-  Promise<int>> promise;
-
-  // extract the Future before we move out of the Promise
-  auto f = promise.getFuture();
-  
-  // *mutable* lambda, because setValue is not const on Promise
-  fooOldFashioned(arg, [moveP = std::move(promise)](int result) mutable {
-    moveP.setValue(result);
-  });
-
-  return f;
-}
- -
NOTE: If you are not using C++14, you will not be able to use move-in-capture as in the example above. Instead, use a folly::MoveWrapper with the promise, and capture that by value.
- -

Though not a hard-and-fast rule, using promises heavily in your code might indicate

- - - -

SharedPromise #

- -

SharedPromise provides the same interface as Promise, but you can extract multiple Futures from it, i.e. you can call getFuture() as many times as you'd like. When the SharedPromise is fulfilled, all of the Futures will be called back. Calls to getFuture() after the SharedPromise is fulfilled return a completed Future. If you find yourself constructing collections of Promises and fulfilling them simultaneously with the same value, consider this utility instead. Likewise, if you find yourself in need of setting multiple callbacks on the same Future (which is indefinitely unsupported), consider refactoring to use SharedPromise to "split" the Future.

Error Handling

Asynchronous code can't employ try/catch exception handling universally, so Futures provides facilities to make error handling as easy and natural as possible. Here's an overview.

Throwing Exceptions #

- -

There are several ways to introduce exceptions into your Futures flow. First, makeFuture<T>() and Promise<T>::setException() can create a failed future from any std::exception, from a folly::exception_wrapper, or from an std::exception_ptr (deprecated):

- -
makeFuture<int>(std::runtime_error("oh no!"));
-makeFuture<int>(folly::make_exception_wrapper<std::runtime_error>("oh no!"));
-makeFuture<int>(std::current_exception());
-
-Promise<int> p1, p2, p3;
-p1.setException(std::runtime_error("oh no!"));
-p2.setException(folly::make_exception_wrapper<std::runtime_error>("oh no!"));
-p3.setException(std::current_exception());
- -

In general, any time you pass a function to a method that returns a Future or fulfills a Promise, you can rest assured that any thrown exceptions (including non-std::exceptions) will be caught and stored. For instance,

- -
auto f = makeFuture().then([]{
-  throw std::runtime_error("ugh");
-});
- -

is perfectly valid code. The exception will be caught and stored in the resultant Future.

- -

Methods that behave this way include

- -
    -
  • Future<T>::then() and all its variants
  • -
  • Future<T>::onError(): more on this below
  • -
  • makeFutureTry(): takes a function, executes it, and creates a Future with the result or any thrown exception
  • -
  • Promise<T>::setWith(): similar to makeFutureTry except it fulfills a Promise instead of creating a completed Future
  • -
- -

Catching Exceptions #

- -

There are also several ways to handle exceptions in Futures code.

- -

Using Try #

- -

First, there's the Try abstraction which multiplexes values and exceptions so they can be handled simultaneously in a then() callback:

- -
makeFuture<int>(std::runtime_error("ugh")).then([](Try<int> t){
-  try {
-    auto i = t.value(); // will rethrow
-    // handle success
-  } catch (const std::exception& e) {
-    // handle failure
-  }
-});
-
-// Try is also integrated with exception_wrapper
-makeFuture<int>(std::runtime_error("ugh")).then([](Try<int> t){
-  if (t.hasException<std::exception>()) {
-    // this is enough if we only care whether the given exception is present
-  }
-});
-
-makeFuture<int>(std::runtime_error("ugh")).then([](Try<int> t){
-  // we can also extract and handle the exception object
-  // TODO(jsedgwick) infer exception type from the type of the function
-  bool caught = t.withException<std::exception>([](const std::exception& e){
-    // do something with e
-  });
-});
- -

Unfortunately, Try encourages both intertwining success and error logic as well as excessive rethrowing. Thankfully, there's another option.

- -

Using onError() #

- -

Future<T>::onError() allows you to have individual exception handlers as separate callbacks. The parameter you specify for your callback is exactly what onError() will try to catch. The callback will be passed over if the future doesn't contain that exception, otherwise, it will be executed and the T or Future<T> that it returns will become the resultant Future instead.

- -
intGeneratorThatMaybeThrows() // returns Future<int>
-  // This is a good opportunity to use the plain value (no Try)
-  // variant of then()
-  .then([](int i) { 
-    return 10 * i; // maybe we throw here instead
-  })
-  .onError([](const std::runtime_error& e) {
-    // ... runtime_error handling ...
-    return -1;
-  })
-  .onError([](const std::exception& e) {
-    // ... all other exception handling ...
-    return -2;
-  });
- -

You can also use onError() directly with exception_wrapper. One use case for this variant is if you want to handle non-std::exception exceptions.

- -
makeFuture().then([]{
-  throw 42;
-})
-.onError([](exception_wrapper ew){
-  // ...
-});
- -

ensure() #

- -

Future<T>::ensure(F func) is similar to the finally block in languages like Java. That is, it takes a void function and will execute regardless of whether the Future contains a value or an exception. The resultant Future will contain the exception/value of the original Future, unless the function provided to ensure throws, in which case that exception will be caught and propagated instead. For instance:

- -
auto fd = open(...);
-auto f = makeFuture().then([fd]{
-  // do some stuff with the file descriptor
-  // maybe we throw, maybe we don't
-})
-.ensure([fd]{
-  // either way, let's release that fd
-  close(fd);
-});
-
-// f now contains the result of the then() callback, unless the ensure()
-// callback threw, in which case f will contain that exception
- -

Performant Exception Handling #

- -

Under the hood, the Futures use folly::exception_wrapper to store exceptions in a way that minimizes costly rethrows. However, the effectiveness of this mechanism depends on whether exceptions are supplied in a way that enables our library (and exception_wrapper) to maintain type information about your exception. Practically speaking, this means constructing exceptional futures directly instead of throwing. For instance:

- -
// This version will throw the exception twice
-makeFuture()
-  .then([]{
-    throw std::runtime_error("ugh");
-  })
-  .onError([](const std::runtime_error& e){
-    // ...
-  });
-// This version won't throw at all!
-makeFuture()
-  .then([]{
-    // This will properly wrap the exception
-    return makeFuture<void>(std::runtime_error("ugh"));
-  })
-  .onError([](const std::runtime_error& e){
-    // ...
-  });
- -

Likewise, using onError instead of throwing via Try will often reduce rethrows. If you want to use Try, look at Try<T>::hasException() and Try<T>::withException() for ways to inspect and handle exceptions without rethrows.

- -

Be wary of premature optimization, and err towards clean code over minimizing rethrows unless you're sure you need the performance. That said, we will continue to strive to make the cleanest option the most performant one as well.

Compositional Building Blocks

Sometimes chaining and nesting with then() is not enough. Here are some utilities for composing futures.

NOTE: For maximum flexibility, many of the helpers documented below take start and end iterators on a collection. All such functions have overloads that take just the collection by reference and automatically operate on the begin() and end() iterators. You will almost always want to take advantage of this sugar. For instance, collect(futures.begin(), futures.end()) can be written as simply collect(futures).
- -

collectAll() #

- -

collectAll() takes an iterable collection of Future<T>s (or start and end iterators on such a collection) and returns a Future<std::vector<Try<T>>> that will complete once all of the input futures complete. The resultant Future's vector will contain the results of each in the same order in which they were passed. Errors in any component Future will not cause early termination. Input Futures are moved in and are no longer valid. For example, we could fan out and fan in a bunch of RPCs like so:

- -
Future<T> someRPC(int i);
-
-std::vector<Future<T>> fs;
-for (int i = 0; i < 10; i++) {
-  fs.push_back(someRPC(i));
-}
-
-collectAll(fs).then([](const std::vector<Try<T>>& tries){
-  for (const auto& t : tries) {
-    // handle each response
-  }
-});
- -
NOTE: Just as with any then() callback, you could take a Try instead and it would compile. But you shouldn't, because the only way the outer Future can fail is if there's a bug in our library. Save yourself some typing and skip the Try. This advice also applies to all of the compositional operations below whose Future types contain inner Trys (i.e. everything except for collect() and map()).
- -

collectAll() variadic #

- -

There is also a variadically templated flavor of collectAll() that allows you to mix and match different types of Futures. It returns a Future<std::tuple<Try<T1>, Try<T2>, ...>>. For example:

- -
Future<int> f1 = ...;
-Future<string> f2 = ...;
-collectAll(f1, f2).then([](const std::tuple<Try<int>, Try<string>>& t){
-  int i = std::get<0>(tup).value();
-  string s = std::get<1>(tup).value();
-  // ...
-});
- -

collect() #

- -

collect() is similar to collectAll(), but will terminate early if an exception is raised by any of the input Futures. Therefore, the returned Future is of type std::vector<T>, unless T is void, in which case the returned Future is void. Like collectAll(), input Futures are moved in and are no longer valid, and the resulting Future's vector will contain the results of each input Future in the same order they were passed in (if all are successful). For instance:

- -
collect(fs).then([](const std::vector<T>& vals){
-  for (const auto& val : vals) {
-    // handle each response
-  }
-})
-.onError([](const std::exception& e) {
-  // drat, one of them failed
-});
-
-// Or using a Try:
-collect(fs).then([](const Try<std::vector<T>>& t){
- // ...
-});
-
-// If fs are void Futures, there's nothing to take in your callback:
-collect(fs).then([]{
-  // ...
-});
- -

collectN() #

- -

collectN, like collectAll(), takes a collection of Futures, or a pair of iterators thereof, but it also takes a size_t N and will complete once N of the input futures are complete. It returns a Future<std::vector<std::pair<size_t, Try<T>>>>. Each pair holds the index of the corresponding Future in the original collection as well as its result, though the pairs themselves will be in arbitrary order. Like collectAll(), collectN() moves in the input Futures, so your copies are no longer valid. If multiple input futures complete "simultaneously" or are already completed, winners are chosen but the choice is undefined.

- -
// Wait for 5 of the input futures to complete
-collectN(fs, 5,
-  [](const std::vector<std::pair<size_t, Try<int>>>& tries){
-    // there will be 5 pairs
-    for (const auto& pair : tries) {
-      size_t index = pair.first;
-      int result = pair.second.value();
-      // ...
-    }
-  });
- -

collectAny() #

- -

collectAny() also takes a collection of Futures (or a pair of iterators thereof), but it completes as soon as any of the input Futures completes. It returns a Future<std::pair<size_t, Try<T>>> which holds the index of the first completed Future along with its result. The input futures are moved in, so your copies are no longer valid. If multiple input futures complete "simultaneously" or are already completed, a winner is chosen but the choice is undefined. For example:

- -
collectAny(fs, [](const std::pair<size_t, Try<int>>& p){
-  size_t index = p.first;
-  int result = p.second.value();
-  // ...
-});
- -

map() #

- -

map() is the Futures equivalent of the higher order function map. It takes a collection of Future<A> (or a pair of iterators thereof) and a function that can be passed to Future<A>::then(), and in turn calls then() with the function on each input Future. It returns a vector of the resultant Futures in the order they were passed in. This is simple sugar for:

- -
std::vector<Future<A>> fs;
-std::vector<Future<B>> fs2;
-for (auto it = fs.begin(); it < fs.end(); it++) {
-  fs2.push_back(it->then(func));
-}
- -

For instance, say you have some expensive RPC that fetches an int and you'd like to do expensive processing on each of many calls to this RPC. collect() or collectAll() might not be wise since they wait for all the results to be ready, while you'd rather process the integers as they arrive. You could use map() in this scenario:

- -
auto fs2 = map(fs, expensiveProcessingFunc);
-// You probably now want to wait for all of these to complete. Call
-// collect() or collectAll() on fs2 to obtain such a Future.
- -

reduce() #

- -

reduce() is the Futures equivalent of the higher order function fold (foldl, specifically). It takes a collection of Future<A> (or a pair of iterators thereof), an initial value of type B, and a function taking two arguments - the reduced value of type B and the next result from the collection of Future<A>. The function must return either B or Future<B>. reduce(), in turn, returns a Future<B>. The function will be applied to the initial value and the result of the first Future, and then to the result of that initial application and the result of the second Future, and so on until the whole collection of Futures has been reduced or an unhandled exception is hit.

- -

The second argument to the reducing function can be either A or Try<A>, depending on whether you want to handle exceptions from the input Futures. If there is an exception in an input Future and you don't take a Try, the reduce operation will short circuit with that exception. Any exception thrown in the reducing function will similarly short circuit the whole operation.

- -

For instance, if you have a collection of Future<int> and you want a Future<bool> that contains true if and only if all the input ints are equal to zero, you might write:

- -
reduce(fs, true, [](bool b, int i){
-  // You could also return a Future<bool> if you needed to
-  return b && (i == 0); 
-})
-.then([](bool result){
-  // result is true if all inputs were zero
-});
-// You could use onError or Try here in case one of your input Futures
-// contained an exception or if your reducing function threw an exception 
- -

To demonstrate the exception handling case, suppose you have a collection of Future<T> and you want a Future<bool> that contains true if all the input Futures are non-exceptional:

- -
reduce(fs, true, [](bool b, Try<T> t){
-  return b && t.hasValue();
-})
-.then([](bool result){
-  // result is true if all inputs were non-exceptional
-});
- -

And finally one example where we're not reducing to a bool - here's how you might calculate the sum of a collection of Future<int>:

- -
reduce(fs, 0, [](int a, int b){
-  return a + b;
-})
-.then([](int sum){
-  // ...
-});
- -

See the reduce() tests in the Future tests for a more complete catalog of possibilities.

- -

unorderedReduce() #

- -

Like reduce(), but consumes Futures in the collection as soon as they become ready. Use this if your function doesn't depend on the order of the Futures in the input collection. See the tests for examples.

- -

window() #

- -

window() is a sliding window implementation for Futures. It takes a collection of T (or a pair of iterators thereof), a function taking a T&& and returning a Future<S>, and a window size n. window() will create up to n Futures at a time using the function. As Futures complete, new Futures are created until the collection is exhausted.

- -

It ensures that at any given time, no more than n Futures are being processed.

- -

Combine with collectAll, reduce or unorderedReduce. See the tests for examples.

- -

Other Possibilities #

- -

There are a number of other possibilities for composing multiple Futures which we'll probably get around to at some point. If any of these seem like they would come in handy, let us know or better yet submit a diff:

- -
    -
  • filter()
  • -
  • <your suggestion here>
  • -

Multithreading and via()

What to know and what to watch out for when using futures in a multithreaded environment, and how to control your threading model.

Futures are thread safe... with a catch. #

- -

The core mutating operations on Futures and Promises are thread safe, insofar as they will throw exceptions if misused (usually, this means being called more than once, including simultaneous calls from different threads). For example, then(), onError() and other methods that set callbacks on Futures will throw exceptions if called twice. The same goes for fulfilling Promises via setValue() and setException().

- -

So what's the catch? Let's look at the following example of multithreaded Futures code:

- -
// Thread A
-Promise<void> p;
-auto f = p.getFuture();
-
-// Thread B
-f.then(x).then(y);
-
-// Thread A
-p.setValue();
- -

In which thread are x and y executed? Unfortunately, it depends. There is a race between setting the callbacks and fulfilling the promise. If setting the callbacks wins, they will be executed in thread A when the Promise is fulfilled. If setting the value wins, they will be executed in thread B as soon as they are set. If setValue() sneaks in at just the right time between the two then() calls, then x will be executed in thread A and y will be executed in thread B. You could imagine that this nondeterminism might become unwieldy or downright unacceptable. Thankfully, there's a mechanism to resolve this race and give you fine-grained control over your execution model.

- -

via() to the rescue #

- -

Futures have a method called via() which takes an Executor. Executor is a simple interface that requires only the existence of an add(std::function<void()> func) method which must be thread safe and must execute the provided function somehow, though not necessarily immediately. via() guarantees that a callback set on the Future will be executed on the given Executor. For instance:

- -
makeFuture()
-  .then(x)
-  .via(exe1).then(y)
-  .via(exe2).then(z);
- -

In this example, y will be executed on exe1, and z will be executed on exe2. This is a fairly powerful abstraction. It not only solves the above race, but gives you clear, concise, and self-documenting control over your execution model. One common pattern is having different executors for different types of work (e.g. an IO-bound pool spinning on event bases doing your network IO and a CPU-bound thread pool for expensive work) and switching between them with via().

- -

There is also a static function via() that creates a completed Future<void> that is already set up to call back on the provided Executor.

- -

Or, pass an Executor to then() #

- -

An alternative to via() is to pass an Executor as the first parameter to then(), which causes the callback to be executed via that Executor. Unlike via() the Executor is not sticky, it only applies for this callback. See the docblock for more details and caveats.

- -

Executor implementations #

- -

via() wouldn't be of much use without practical implementations around. We have a handful, and here's a (possibly incomplete) list.

- -
    -
  • ThreadPoolExecutor is an abstract thread pool implementation that supports resizing, custom thread factories, pool and per-task stats, NUMA awareness, user-defined task expiration, and Codel task expiration. It and its subclasses are under active development. It currently has two implementations:
  • -
  • folly's EventBase is an Executor and executes work as a callback in the event loop
  • -
  • ManualExecutor only executes work when manually cranked. This is useful for testing.
  • -
  • InlineExecutor executes work immediately inline
  • -
  • QueuedImmediateExecutor is similar to InlineExecutor, but work added during callback execution will be queued instead of immediately executed
  • -
  • ScheduledExecutor is a subinterface of Executor that supports scheduled (i.e. delayed) execution. There aren't many implementations yet, see T5924392
  • -
  • FutureExecutor wraps another Executor and provides Future<T> addFuture(F func) which returns a Future representing the result of func. This is equivalent to via(executor).then(func) and the latter should probably be preferred.
  • -

Timeouts and related features

Futures provide a number of timing-related features. Here's an overview.

Timing implementation #

- -

Timing resolution #

- -

The functions and methods documented below all take a Duration, which is an alias for std::chrono::milliseconds. Why not allow more granularity? Simply put, we can't guarantee sub-millisecond resolution and we don't want to lie to you.

- -

The TimeKeeper interface #

- -

Most timing-related methods also optionally take a default implementation uses a folly::HHWheelTimer in a dedicated EventBase thread to manage timeouts.

- -

within() #

- -

Future<T>::within() returns a new Future that will complete with the provided exception (by default, a TimedOut exception) if it does not complete within the specified duration. For example:

- -
Future<int> foo();
-
-// f will complete with a TimedOut exception if the Future returned by foo()
-// does not complete within 500 ms
-f = foo().within(Duration(500));
-
-// Same deal, but a timeout will trigger the provided exception instead
-f2 = foo().within(Duration(500), std::runtime_error("you took too long!"));
- -

onTimeout() #

- -

Future<T>::onTimeout() lets you simultaneously set up a timeout and a timeout handler. For example:

- -
Future<int> foo();
-foo()
-  .onTimeout(Duration(500), []{
-    // You must maintain the resultant future's type
-    // ... handle timeout ...
-    return -1;
-  })
-  .then(...);
- -

The astute reader might notice that this is effectively syntactic sugar for

- -
foo()
-  .within(Duration(500))
-  .onError([](const TimedOut& e) {
-    // handle timeout
-    return -1;
-  })
-  .then(...);
- -

get() and wait() with timeouts #

- -

get() and wait(), which are detailed in the Testing article, optionally take timeouts:

- -
Future<int> foo();
-// Will throw TimedOut if the Future doesn't complete within one second of
-// the get() call
-int result = foo().get(Duration(1000));
-
-// If the Future doesn't complete within one second, f will remain
-// incomplete. That is, if a timeout occurs, it's as if wait() was
-// never called.
-Future<int> f = foo().wait(Duration(1000));
- -

delayed() #

- -

Future<T>::delayed() returns a new Future whose completion is delayed for at least the specified duration. For example:

- -
makeFuture()
-  .delayed(Duration(1000))
-  .then([]{
-    // This will be executed when the original Future has completed or when
-    // 1000ms has elapsed, whichever comes last.
-  });
- -

futures::sleep() #

- -

sleep() returns a void Future that will complete after the specified duration. For example:

- -
futures::sleep(Duration(1000)).then([]{
-  // This will be executed after 1000ms
-});

Interrupts and Cancellations

Interrupts are a mechanism for Future holders to send a signal to Promise holders. Here's how to use them.

Let's say that your Futures code kicks off some long, expensive operation in another thread. A short while later, something comes up that obviates the need for the result of that operation. Are those resources gone forever? Not necessarily. Enter interrupts.

- -

Interrupts allow Future holders to send signals in the form of exceptions to Promise holders, who are free to handle the interrupt as they please (or not at all). For example:

- -
Promise<int> p;
-p.setInterruptHandler([](const exception_wrapper& e){
-  // Handle the interrupt. For instance, we could just fulfill the Promise
-  // with the given exception:
-  p.setException(e);
-  
-  // Or maybe we want the Future to complete with some special value
-  p.setValue(42);
-
-  // Or maybe we don't want to do anything at all! Including not setting
-  // this handler in the first place.
-});
-
-auto f = p.getFuture();
-// The Future holder can now send an interrupt whenever it wants via raise().
-// If the interrupt beats out the fulfillment of the Promise *and* there is
-// an interrupt handler set on the Promise, that handler will be called with
-// the provided exception
-f.raise(std::runtime_error("Something went awry! Abort!"));
-
-// cancel() is syntactic sugar for raise(FutureCancellation())
-f.cancel();
- -

Going forward, we'd like to integrate interrupts with major Future interface provides as a way to cancel RPCs and the like, but that's not in place yet. This is a bleeding edge feature—please let us know your use cases so that we can iterate!

Testing

Testing futures-based code does not have to be a pain. Here are some tips and idiomatic approaches.

Extracting values synchronously #

- -
NOTE: The tests in this article are written using the gtest framework.
- -

Let's say we want to test the following interface:

- -
Future<bool> isPrime(int n);
- -

We could make a couple of calls and set expectations on the resultant futures via value():

- -
EXPECT_TRUE(isPrime(7).value());
-EXPECT_FALSE(isPrime(8).value());
- -

But what if isPrime() is asynchronous (e.g. makes an async call to another service that computes primeness)? It's now likely that you'll call value() before the Future is complete, which will throw a FutureNotReady exception.

- -

A naive approach is to spin until the Future is complete:

- -
// Spin until ready. Gross. Don't do this.
-auto f = isPrime(7);
-while (!f.isReady()) {}
-EXPECT_TRUE(f.value());
- -

Thankfully, we have some better options in the form of Future<T>::get() and Future<T>::wait().

- -

get() #

- -

T Future<T>::get() blocks until the Future is complete and either returns a moved out copy of the value or throws any contained exception. You can use it like so.

- -
EXPECT_TRUE(isPrime(7).get());
- -

Keep in mind that some other thread had better complete the Future, because the thread that calls get() will block. Also, get() optionally takes a timeout after which its throws a TimedOut exception. See the Timeouts article for more information.

- -

wait() #

- -

Future<T> Future<T>::wait() is similar to get() in that it blocks until the Future is complete. However, instead of returning a value or throwing an exception, it returns a new completed Future with the result of the original Future. One use case is when you'd rather not have the throwing behavior of get() so that you can check for exceptions separately without a try/catch. For example:

- -
auto f = isPrime(7).wait();
-EXPECT_FALSE(f.getTry().hasException());
-EXPECT_TRUE(f.value());
- -

Like get(), wait() optionally takes a timeout. Again, see the Timeouts article.

- -

getVia() and waitVia() #

- -

T Future<T>::getVia(DrivableExecutor*) and Future<T> Future<T>::waitVia(DrivableExecutor*) have the same semantics as get() and wait() except that they drive some Executor until the Future is complete. ManualExecutor. These are simple but useful sugar for the following common pattern:

- -

Given this:

- -
auto f = doAsyncWorkOnEventBase(&eventBase);
- -

Don't do this:

- -
while (!f.isReady()) {
-  eb.loop();
-}
- -

Do one of these instead:

- -
auto val = f.getVia(&eventBase);
-// or
-f.waitVia(&eb).then([](Value val){ ... });
- -

Using gmock #

- -

Google Mock is a powerful mocking framework for writing and using C++ mock classes. Unfortunately, Gmock requires that the parameters and return types of mocked functions and methods are copyable. You're likely to hit this issue when mocking Futures code because Futures (and, less importantly, Promises) are noncopyable, and many of your interfaces will return Futures and some will even be passed Futures.

- -

The canonical approach to mocking interfaces that involve noncopyable objects is to override your interface with a dummy method that simply calls a mock method that has had the noncopyable components stripped or replaced. For Futures, this usually means returning/passing contained values directly and synchronously, which shouldn't be a problem since your mocks won't actually be asynchronous. Here is a very contrived but demonstrative example:

- -
// The async interface we want to mock
-class AsyncClient {
- public:
-  virtual Future<int> foo(int i);
-};
-
-// The mock implementation
-class MockAsyncClient : public AsyncClient {
- public:
-  // Declare a mock method foo_ that takes an int and returns an int
-  MOCK_METHOD1(foo_, int(int));
-
-  // Plug the mock into an override of the interface
-  Future<int> foo(int i) override {
-    // Lift the result back into a Future before returning
-    return makeFuture<int>(foo_(i));
-  }
-};
-
-// Let's say that we're testing a class MyProxy that simply forwards foo()
-// calls to AsyncClient and returns the result
-class MyProxy {
- public:
-  Future<int> foo(int i) {
-    return client->foo(i);
-  }
-  void setClient(AsyncClient* client);
- private:
-  AsyncClient* client;
-};
-
-// Now, in our testing code
-MyProxy proxy;
-MockAsyncClient mockClient;
-// Inject the mock
-proxy.setClient(&mockClient)
-// Set an expectation on the mock to be called with 42 and return 84
-EXPECT_CALL(mockClient, foo_(42)).WillOnce(Return(84));
-// Trigger the call
-auto f = MyProxy.foo(42);
-// If everything has been mocked out synchronously, we can just check the
-// value of the future directly
-EXPECT_EQ(84, f.value());

Pitfalls

EventBase, EventBaseManager, Executor #

- -

It's not uncommon to hit a snag (especially when using via()) where you're hanging for (a) being on the wrong thread (b) talking to an EventBase which is not actually spinning (loopForever).

- -

Some tips:

- -
    -
  • evb->isInRunningEventBase()
  • -
  • evb->isRunning()
  • -
- -

Lambda Arguments #

- -

The danger with lambdas is you'll try to read a variable that's gone

- -
Object obj = ...;
-return future1.then([&] {
-    // ..work..
-    obj.method();
-      // woops object is gone from the 
-      // stack when this actually runs
-});
- -

Sometimes it makes sense to copy inputs. Sometimes that's too expensive and a shared_ptr is best. Sometimes the nature of things lends itself to the contract "this won't go away" and you take a raw pointer, but this should only be used when it's a very natural fit. In particular, you don't want people wishing you took a shared pointer and having to do something like this to work around it:

- -
auto foo = make_shared<Foo>();
-yourFunction(foo.get(),
-  [foo]{ 
-     /* callback doesn't use foo, but captures the 
-      * shared pointer to keep it alive 
-      */
-});
- -

In general: -prefer taking inputs by value if they're small enough -if inputs are big (measurably expensive to copy), then keep them on the heap and prefer a shared_ptr -if you are really sure you need to get more fancy, put on your wizard hat and go to it ;)

- -
Object obj = ...;
-return future1.then([obj] {  // capture by value
-    // ..work..
-    obj.method();
-      // works on copy of obj
-});
- -

If Object is large:

- -
auto optr = makeShared<Object>(...);
-return future1.then([optr] {  // copy ptr, use count = 2
-    // ..work..
-    optr->method();
-      // works on original object
-    // use-count for optr goes to 0 and object destructs
-});

Future as a Monad

A semi-formal and totally optional analysis of Future as a monad.

Future is a monad. You don't need to know this or what it means to use Futures, but if you are curious, want to understand monads better, or eat functional flakes for breakfast, then keep reading this extremely extracurricular document.

- -

Let's review the definition of a monad. Formal definitions are mathematical and/or in Haskellese and therefore opaque to imperative mortals. But here's a simplified description using a subset of Haskell type notation that is useful but not confusing:

- -
-- "unit" is a function that takes a value and wraps it in the monad type.
--- Haskellers call this "return" as some kind of sick inside joke.
-unit :: a -> m a
-
--- "bind" is a function that takes a monad, and a function that takes a value
--- and returns another monad. Haskellers call this ">>=" because they are
--- vying to unseat perl from the throne of illegibility.
-bind :: m a -> (a -> m b) -> m b
- -

Monads must also satisfy these three axioms:

- -
-- Left Identity
-unit a `bind` f ≡ f a
--- Right Identity
-m `bind` unit ≡ m
--- Associativity
-(m `bind` f) `bind` g ≡ m `bind` (\x -> f x `bind` g)
- -

I won't try to explain that, there are many blog posts and wiki pages that try to do that. Instead, I'll substitute the equivalent Future monad expressions, and the whole thing will (probably) start to make sense. First, a simplified Future type:

- -
template <class A>
-struct Future {
-  // The constructor that takes a value is "unit"
-  Future(A);
-
-  // "then" is "bind"
-  template <class B>
-  Future<B> then(std::function<Future<B>(A));
-
-  ...
-};
-
-// "makeFuture" is also "unit", and we will need it because constructors can't
-// really be converted to std::function (AFAIK)
-template <class A>
-Future<A> makeFuture(A);
- -

Now, the three axioms (Futures don't define operator== but you get the idea):

- -
// Left Identity
-A a;
-Future<A>(a).then(f) == f(a)
-
-// Right Identity
-Future<A> m;
-m.then(makeFuture<A>) == m
-
-// Associativity
-Future<A> m;
-std::function<Future<B>(A)> f;
-std::function<Future<C>(B)> g;
-m.then(f).then(g) == m.then([](A x) { return f(x).then(g); })
- -

So, in plain english this says a monad like Future has a way to get stuff in the monad (unit/makeFuture), and a way to chain things together (bind/then). unit semantics are unsurprising, and chaining is the same as nesting. Something that behaves this way is a monad, and Future is a monad.

- -
Remember how Futures do more than just hold values? The nature of the underlying asynchronous operations (usually I/O) generally includes side effects, and this breaks our pure formalism. You may or may not be able to make your async operations (observable) side-effect free, but you can make your intermediate Future callbacks functionally pure (aka value semantics), and if you do you will be happier than if you mutate state. But I won't beat that dead horse here—I know you will probably mutate state anyway because you're a perf-conscious C++ developer and speed trumps safety. But do try to minimize it.
- -

Ok, so now we know Future is a monad. What can we do with this newfound power? Knowledge is power, right? Well, you can brag to your friends, for one thing. C++ doesn't really provide any concrete reusable tools for things that are monads. There's no do-blocks, or some generic monad-aware functional toolkit that includes map, filter, fold, etc. But what you do get is a way of thinking about and reasoning about your Futures that transcends our own little implementation, and doesn't require that you grok all the opaque internals of the implementation to do it.

- -

But mostly it makes you cool.

- -

Kleisli Composition (extra extra credit) #

- -

If "associative" doesn't look associative to you, then you are very astute. Congratulations! You win a maths unicorn. -The three laws refer to a different formulation of the axioms, in terms of the Kleisli Composition operator (>=>), which basically says compose two monad-making functions in the obvious way.

- -
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
-
--- Left Identity
-unit >=> g ≡ g
--- Right Identity
-f >=> unit ≡ f
--- Associativity
-(f >=> g) >=> h ≡ f >=> (g >=> h)
- -

We accidentally implemented this operator, and called it chain. Then we removed it in favor of Future::thenMulti. But it totally existed, so use your imagination:

- -
// Left Identity
-chain(makeFuture, g) ≡ g
-// Right Identity
-chain(f, makeFuture) ≡ f
-// Associativity
-chain(chain(f, g), h) ≡ chain(f, chain(g, h)) // and chain(f, g, h)
- -

Further reading #

- -
    -
  • https://wiki.haskell.org/Monad_laws
  • -
  • http://learnyouahaskell.com/a-fistful-of-monads
  • diff --git a/folly/futures/README.md b/folly/futures/README.md index baac85fd..3f197ca5 100644 --- a/folly/futures/README.md +++ b/folly/futures/README.md @@ -1,264 +1,1065 @@ -# folly::Futures +

    Futures

    Futures is a framework for expressing asynchronous code in C++ using the Promise/Future pattern.

    Overview #

    -Futures 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 Futures. +

    Folly Futures is an async C++ framework inspired by Twitter's Futures implementation in Scala (see also Future.scala, Promise.scala, and friends), and loosely builds upon the existing but anemic Futures code found in the C++11 standard (std::future) and boost::future (especially >= 1.53.0). +Although inspired by the C++11 std::future interface, it is not a drop-in replacement because some ideas don't translate well enough to maintain API compatibility.

    -The primary semantic differences are that folly's Futures and Promises are not threadsafe; and as does `boost::future`, folly::Futures support continuing callbacks (`then()`) and there are helper methods `collectAll()` and `collectAny()` which are important compositional building blocks. +

    The primary difference from std::future is that you can attach callbacks to Futures (with then()), which enables sequential and parallel composition of Futures for cleaner asynchronous code.

    -## Brief Synopsis +

    Brief Synopsis #

    -```C++ -#include -using namespace folly; -using namespace std; +
    #include <folly/futures/Future.h>
    +using namespace folly;
    +using namespace std;
     
    -void foo(int x) {
    -  // do something with x
    -  cout << "foo(" << x << ")" << endl;
    -}
    +void foo(int x) {
    +  // do something with x
    +  cout << "foo(" << x << ")" << endl;
    +}
     
    -// ...
    +// ...
     
    -  cout << "making Promise" << endl;
    -  Promise p;
    -  Future f = p.getFuture();
    -  f.then(
    -    [](Try&& t) {
    -      foo(t.value());
    -    });
    -  cout << "Future chain made" << endl;
    +  cout << "making Promise" << endl;
    +  Promise<int> p;
    +  Future<int> f = p.getFuture();
    +  f.then(foo);
    +  cout << "Future chain made" << endl;
     
    -// ... now perhaps in another event callback
    +// ... now perhaps in another event callback
     
    -  cout << "fulfilling Promise" << endl;
    -  p.setValue(42);
    -  cout << "Promise fulfilled" << endl;
    -```
    +  cout << "fulfilling Promise" << endl;
    +  p.setValue(42);
    +  cout << "Promise fulfilled" << endl;
    -This would print: +

    This would print:

    + +
    making Promise
    +Future chain made
    +fulfilling Promise
    +foo(42)
    +Promise fulfilled

    Brief Guide

    This brief guide covers the basics. For a more in-depth coverage skip to More Details or the appropriate section.

    + +

    Let's begin with an example using an imaginary simplified Memcache client interface:

    + +
    using std::string;
    +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
    +    string value;
    +  };
    +
    +  GetReply get(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():

    + +
    int get(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. Very performant code can be written with an API like this, but for nontrivial applications the code devolves into a special kind of spaghetti code affectionately referred to as "callback hell".

    + +

    The Future-based API looks like this:

    + +
    Future<GetReply> get(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.:

    + +
    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.

    + +
    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:

    + +
    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.

    + +
    vector<Future<GetReply>> futs;
    +for (auto& key : keys) {
    +  futs.push_back(mc.get(key));
    +}
    +auto all = collectAll(futs.begin(), futs.end());
    +
    +vector<Future<GetReply>> futs;
    +for (auto& key : keys) {
    +  futs.push_back(mc.get(key));
    +}
    +auto any = collectAny(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 collectN() for when you need some.)

    + +

    Second, we can attach callbacks to a Future, and chain them together monadically. An example will clarify:

    + +
    Future<GetReply> fut1 = mc.get("foo");
    +
    +Future<string> fut2 = fut1.then(
    +  [](GetReply reply) {
    +    if (reply.result == MemcacheClient::GetReply::Result::FOUND)
    +      return reply.value;
    +    throw SomeException("No value");
    +  });
    +
    +Future<void> fut3 = fut2
    +  .then([](string str) {
    +    cout << str << endl;
    +  })
    +  .onError([](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.

    + +

    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. Futures have a blocking method called wait() that does exactly that and optionally takes a timeout.

    + +

    Futures are 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.

    + +
    // 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.

    + +
    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. If after z you want to get back to the current thread, you need to get there via an executor. Another way to express this is using an overload of then that takes an Executor:

    + +
    aFuture
    +  .then(x)
    +  .then(e1, y1, y2)
    +  .then(e2, z);
    + +

    Either way, there is no ambiguity about which thread will execute y1, y2, or z.

    + +

    You can still have a race after via if you break it into multiple statements, e.g. in this counterexample:

    + +
    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 fulfill it with a value or an exception. Example:

    + +
    Promise<int> p;
    +Future<int> f = p.getFuture();
    +
    +f.isReady() == false
    +
    +p.setValue(42);
    +
    +f.isReady() == true
    +f.value() == 42
    + +

    and an exception example:

    + +
    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 setWith which takes a function and automatically captures exceptions, e.g.

    + +
    Promise<int> p;
    +p.setWith([]{
    +  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
    +});

    More Details

    Let's look at a contrived and synchronous example of Futures.

    + +
    Promise<int> p;
    +Future<int> f = p.getFuture();
    +// ...
    +p.setValue(42); // or setException(...)
    +cout << f.value(); // prints 42
    + +

    First, we create a Promise object of type int. This object is exactly what it sounds like—a pledge to provide an int (or an exception) at some point in the future.

    + +

    Next, we extract a Future object from that promise. You can think of futures as handles on promises - they provide a way to access that int when the promise is fulfilled.

    + +

    Later, when the promise is fulfilled via setValue() or setException(), that int is accessible via the future's value() method. That method will throw if the future contains an exception.

    + +

    Setting callbacks with then() #

    + +

    Ok, great, so now you're wondering what these are actually useful for. Let's consider another way to follow up on the result of a Future once its corresponding Promise is fulfilled—callbacks! Here's a snippet that is functionally equivalent to the one above:

    + +
    Promise<int> p;
    +Future<int> f = p.getFuture();
    +
    +f.then([](int i){
    +  cout << i;
    +});
    +
    +p.setValue(42);
    + +

    That then() method on futures is the real bread and butter of Futures code. It allows you to provide a callback which will be executed when that Future is complete. Note that while we fulfill the promise after setting the callback here, those operations could be swapped. Setting a callback on an already completed future executes the callback immediately.

    + +

    In this case, the callback takes a value directly. If the Future contained an exception, the callback will be passed over and the exception will be propagated to the resultant Future - more on that in a second. Your callback may also take a Try, which encapsulates either an exception or a value of its templated type.

    + +
    f.then([](Try<int> const& t){
    +  cout << t.value();
    +});
    + +
    NOTE: Do not use Try unless you are actually going to do exception handling in your callback. It is much cleaner and often more performant to take the value directly when you can. If you want to do exception handling, there still might be better options than Try. See Error Handling.
    + +

    The real power of then() is that it returns a Future of the type that the callback returns and can therefore be chained and nested with ease. Let's consider another example:

    + +
    Future<string> f2 = f.then([](int i){
    +  return folly::to<string>(i);
    +});
    +
    +f2.then([](string s){ /* ... */ });
    + +

    Here, we convert that int to a string in the callback and return the result, which results in a Future<string> that we can set further callbacks on. I've created a named variable f2 to demonstrate types but don't hesitate to chain futures directly:

    + +
    auto finalFuture = getSomeFuture()
    +  .then(...)
    +  .then(...)
    +  .then(...);
    + +

    That's all great, but this code is still synchronous. These constructs truly become useful when you start to chain, nest, and compose asynchronous operations. Let's say you instead have some remote service that converts your integers to strings for you, and that you also have a client with Future interfaces (i.e. interfaces that return Futures). Now let's leverage the fact that then() also allows you to return Future<T> from inside your callbacks as well as just T:

    + +
    Future<string> f2 = f.then([](int i){
    +  return getClient()->future_intToString(i); // returns Future<string>
    +});
    +
    +f2.then([](Try<string> const& s){ ... });
    + +

    In general, your code will be cleaner if you return T from your callbacks and only switch to returning Future<T> when necessary (i.e. when there is a nested call to a future-returning function).

    + +

    Futures, Promises, and move semantics #

    + +

    Futures and Promises are movable but non-copyable. This preserves the invariant of a one-to-one mapping between a Promise and a Future and as a side effect encourages performant code. There is a piece of heap-allocated shared state underlying each promise-future pair—keep this in mind as a bare minimum performance cost.

    + +

    Synchronously creating and completing futures #

    + +

    Synchronously entering and exiting the futures paradigm can be useful, especially in tests, so the following utilities are available:

    + +
      +
    • Create already-completed futures with makeFuture<T>(), which takes a T&& (or an exception, more info here). If you pass T&& the type is inferred and you don't have to specify it.
    • +
    • Extract a future's T value with Future<T>::get(). This method is blocking, so make sure that either your future is already completed or that another thread will complete the future while the calling thread blocks. get() can also take a timeout—see Timeouts.
    • +
    • Perform a blocking wait on a Future with Future<T>::wait(). This is just like get() but it instead of extracting the value or throwing the exception, wait() returns a new Future with the result of the input Future. Like get(), wait() can also take a timeout—see Timeouts.
    • +
    • getVia() and waitVia(), which are like get() and wait() except that they drive some Executor (say, an EventBase) until the Future is complete. See Testing for more.
    • +
    + +
    NOTE: makeFuture(), get(), wait(), and friends are especially handy in tests and are documented further in the Testing article.
    + +

    Overloads of then() #

    + +

    Above are demonstrations of variants of then() whose callbacks

    + +
      +
    • return Future<T> or T
    • +
    • take T const& or Try<T> const& (also possible are T, Try<T>, T&&, and Try<T>&&)
    • +
    + +
    NOTE: The preferred pattern is—when possible—to use value semantics (take a T or Try<T>). If your type is expensive to copy or can't be copied, take a reference. (e.g. T const& or Try<T> const&) If you need move semantics, an lvalue reference or rvalue reference is the same in this situation. Use whichever you stylistically prefer.
    + +

    The flexibility doesn't end there. There are also overloads so that you can bind global functions, member functions, and static member functions to then():

    + +
    void globalFunction(Try<int> const& t);
    +
    +struct Foo {
    +  void memberMethod(Try<int> const& t);
    +  static void staticMemberMethod(Try<int> const& t);
    +};
    +Foo foo;
    +
    +// bind global function
    +makeFuture<int>(1).then(globalFunction);
    +// bind member method
    +makeFuture<int>(2).then(&Foo::memberMethod, &foo);
    +// bind static member method
    +makeFuture<int>(3).then(&Foo::staticMemberMethod);
    + +

    A note on Promises #

    + +

    Generally speaking, the majority of your futures-based code will deal with Futures alone and not Promises—calling Future-returning interfaces, composing callbacks on them, and eventually returning another Future. Promises are most useful when you're wrapping some lower level asynchronous interface so that you can return a Future:

    + +
    void fooOldFashioned(int arg, std::function<int(int)> callback);
    +
    +Future<int> foo(int arg) {
    +  auto promise = std::make_shared<Promise<int>>();
    +
    +  fooOldFashioned(arg, [promise](int result) {
    +    promise->setValue(result);
    +  });
    +
    +  return promise->getFuture();
    +}
    + +

    Though not a hard-and-fast rule, using promises heavily in your code might indicate

    + + + +

    SharedPromise #

    + +

    SharedPromise provides the same interface as Promise, but you can extract multiple Futures from it, i.e. you can call getFuture() as many times as you'd like. When the SharedPromise is fulfilled, all of the Futures will be called back. Calls to getFuture() after the SharedPromise is fulfilled return a completed Future. If you find yourself constructing collections of Promises and fulfilling them simultaneously with the same value, consider this utility instead. Likewise, if you find yourself in need of setting multiple callbacks on the same Future (which is indefinitely unsupported), consider refactoring to use SharedPromise to "split" the Future.

    Error Handling

    Asynchronous code can't employ try/catch exception handling universally, so Futures provides facilities to make error handling as easy and natural as possible. Here's an overview.

    Throwing Exceptions #

    + +

    There are several ways to introduce exceptions into your Futures flow. First, makeFuture<T>() and Promise<T>::setException() can create a failed future from any std::exception, from a folly::exception_wrapper, or from an std::exception_ptr (deprecated):

    + +
    makeFuture<int>(std::runtime_error("oh no!"));
    +makeFuture<int>(folly::make_exception_wrapper<std::runtime_error>("oh no!"));
    +makeFuture<int>(std::current_exception());
    +
    +Promise<int> p1, p2, p3;
    +p1.setException(std::runtime_error("oh no!"));
    +p2.setException(folly::make_exception_wrapper<std::runtime_error>("oh no!"));
    +p3.setException(std::current_exception());
    + +

    In general, any time you pass a function to a method that returns a Future or fulfills a Promise, you can rest assured that any thrown exceptions (including non-std::exceptions) will be caught and stored. For instance,

    + +
    auto f = makeFuture().then([]{
    +  throw std::runtime_error("ugh");
    +});
    + +

    is perfectly valid code. The exception will be caught and stored in the resultant Future.

    + +

    Methods that behave this way include

    + +
      +
    • Future<T>::then() and all its variants
    • +
    • Future<T>::onError(): more on this below
    • +
    • makeFutureTry(): takes a function, executes it, and creates a Future with the result or any thrown exception
    • +
    • Promise<T>::setWith(): similar to makeFutureTry except it fulfills a Promise instead of creating a completed Future
    • +
    + +

    Catching Exceptions #

    + +

    There are also several ways to handle exceptions in Futures code.

    + +

    Using Try #

    + +

    First, there's the Try abstraction which multiplexes values and exceptions so they can be handled simultaneously in a then() callback:

    + +
    makeFuture<int>(std::runtime_error("ugh")).then([](Try<int> t){
    +  try {
    +    auto i = t.value(); // will rethrow
    +    // handle success
    +  } catch (const std::exception& e) {
    +    // handle failure
    +  }
    +});
    +
    +// Try is also integrated with exception_wrapper
    +makeFuture<int>(std::runtime_error("ugh")).then([](Try<int> t){
    +  if (t.hasException<std::exception>()) {
    +    // this is enough if we only care whether the given exception is present
    +  }
    +});
    +
    +makeFuture<int>(std::runtime_error("ugh")).then([](Try<int> t){
    +  // we can also extract and handle the exception object
    +  // TODO(jsedgwick) infer exception type from the type of the function
    +  bool caught = t.withException<std::exception>([](const std::exception& e){
    +    // do something with e
    +  });
    +});
    + +

    Unfortunately, Try encourages both intertwining success and error logic as well as excessive rethrowing. Thankfully, there's another option.

    + +

    Using onError() #

    + +

    Future<T>::onError() allows you to have individual exception handlers as separate callbacks. The parameter you specify for your callback is exactly what onError() will try to catch. The callback will be passed over if the future doesn't contain that exception, otherwise, it will be executed and the T or Future<T> that it returns will become the resultant Future instead.

    + +
    intGeneratorThatMaybeThrows() // returns Future<int>
    +  // This is a good opportunity to use the plain value (no Try)
    +  // variant of then()
    +  .then([](int i) { 
    +    return 10 * i; // maybe we throw here instead
    +  })
    +  .onError([](const std::runtime_error& e) {
    +    // ... runtime_error handling ...
    +    return -1;
    +  })
    +  .onError([](const std::exception& e) {
    +    // ... all other exception handling ...
    +    return -2;
    +  });
    + +

    You can also use onError() directly with exception_wrapper. One use case for this variant is if you want to handle non-std::exception exceptions.

    + +
    makeFuture().then([]{
    +  throw 42;
    +})
    +.onError([](exception_wrapper ew){
    +  // ...
    +});
    + +

    ensure() #

    + +

    Future<T>::ensure(F func) is similar to the finally block in languages like Java. That is, it takes a void function and will execute regardless of whether the Future contains a value or an exception. The resultant Future will contain the exception/value of the original Future, unless the function provided to ensure throws, in which case that exception will be caught and propagated instead. For instance:

    + +
    auto fd = open(...);
    +auto f = makeFuture().then([fd]{
    +  // do some stuff with the file descriptor
    +  // maybe we throw, maybe we don't
    +})
    +.ensure([fd]{
    +  // either way, let's release that fd
    +  close(fd);
    +});
    +
    +// f now contains the result of the then() callback, unless the ensure()
    +// callback threw, in which case f will contain that exception
    + +

    Performant Exception Handling #

    + +

    Under the hood, the Futures use folly::exception_wrapper to store exceptions in a way that minimizes costly rethrows. However, the effectiveness of this mechanism depends on whether exceptions are supplied in a way that enables our library (and exception_wrapper) to maintain type information about your exception. Practically speaking, this means constructing exceptional futures directly instead of throwing. For instance:

    + +
    // This version will throw the exception twice
    +makeFuture()
    +  .then([]{
    +    throw std::runtime_error("ugh");
    +  })
    +  .onError([](const std::runtime_error& e){
    +    // ...
    +  });
    +// This version won't throw at all!
    +makeFuture()
    +  .then([]{
    +    // This will properly wrap the exception
    +    return makeFuture<void>(std::runtime_error("ugh"));
    +  })
    +  .onError([](const std::runtime_error& e){
    +    // ...
    +  });
    + +

    Likewise, using onError instead of throwing via Try will often reduce rethrows. If you want to use Try, look at Try<T>::hasException() and Try<T>::withException() for ways to inspect and handle exceptions without rethrows.

    + +

    Be wary of premature optimization, and err towards clean code over minimizing rethrows unless you're sure you need the performance. That said, we will continue to strive to make the cleanest option the most performant one as well.

    Compositional Building Blocks

    Sometimes chaining and nesting with then() is not enough. Here are some utilities for composing futures.

    NOTE: For maximum flexibility, many of the helpers documented below take start and end iterators on a collection. All such functions have overloads that take just the collection by reference and automatically operate on the begin() and end() iterators. You will almost always want to take advantage of this sugar. For instance, collect(futures.begin(), futures.end()) can be written as simply collect(futures).
    + +

    collectAll() #

    + +

    collectAll() takes an iterable collection of Future<T>s (or start and end iterators on such a collection) and returns a Future<std::vector<Try<T>>> that will complete once all of the input futures complete. The resultant Future's vector will contain the results of each in the same order in which they were passed. Errors in any component Future will not cause early termination. Input Futures are moved in and are no longer valid. For example, we could fan out and fan in a bunch of RPCs like so:

    + +
    Future<T> someRPC(int i);
    +
    +std::vector<Future<T>> fs;
    +for (int i = 0; i < 10; i++) {
    +  fs.push_back(someRPC(i));
    +}
    +
    +collectAll(fs).then([](const std::vector<Try<T>>& tries){
    +  for (const auto& t : tries) {
    +    // handle each response
    +  }
    +});
    + +
    NOTE: Just as with any then() callback, you could take a Try instead and it would compile. But you shouldn't, because the only way the outer Future can fail is if there's a bug in our library. Save yourself some typing and skip the Try. This advice also applies to all of the compositional operations below whose Future types contain inner Trys (i.e. everything except for collect() and map()).
    + +

    collectAll() variadic #

    + +

    There is also a variadically templated flavor of collectAll() that allows you to mix and match different types of Futures. It returns a Future<std::tuple<Try<T1>, Try<T2>, ...>>. For example:

    + +
    Future<int> f1 = ...;
    +Future<string> f2 = ...;
    +collectAll(f1, f2).then([](const std::tuple<Try<int>, Try<string>>& t){
    +  int i = std::get<0>(tup).value();
    +  string s = std::get<1>(tup).value();
    +  // ...
    +});
    + +

    collect() #

    + +

    collect() is similar to collectAll(), but will terminate early if an exception is raised by any of the input Futures. Therefore, the returned Future is of type std::vector<T>, unless T is void, in which case the returned Future is void. Like collectAll(), input Futures are moved in and are no longer valid, and the resulting Future's vector will contain the results of each input Future in the same order they were passed in (if all are successful). For instance:

    + +
    collect(fs).then([](const std::vector<T>& vals){
    +  for (const auto& val : vals) {
    +    // handle each response
    +  }
    +})
    +.onError([](const std::exception& e) {
    +  // drat, one of them failed
    +});
    +
    +// Or using a Try:
    +collect(fs).then([](const Try<std::vector<T>>& t){
    + // ...
    +});
    +
    +// If fs are void Futures, there's nothing to take in your callback:
    +collect(fs).then([]{
    +  // ...
    +});
    + +

    collectN() #

    + +

    collectN, like collectAll(), takes a collection of Futures, or a pair of iterators thereof, but it also takes a size_t N and will complete once N of the input futures are complete. It returns a Future<std::vector<std::pair<size_t, Try<T>>>>. Each pair holds the index of the corresponding Future in the original collection as well as its result, though the pairs themselves will be in arbitrary order. Like collectAll(), collectN() moves in the input Futures, so your copies are no longer valid. If multiple input futures complete "simultaneously" or are already completed, winners are chosen but the choice is undefined.

    + +
    // Wait for 5 of the input futures to complete
    +collectN(fs, 5,
    +  [](const std::vector<std::pair<size_t, Try<int>>>& tries){
    +    // there will be 5 pairs
    +    for (const auto& pair : tries) {
    +      size_t index = pair.first;
    +      int result = pair.second.value();
    +      // ...
    +    }
    +  });
    + +

    collectAny() #

    + +

    collectAny() also takes a collection of Futures (or a pair of iterators thereof), but it completes as soon as any of the input Futures completes. It returns a Future<std::pair<size_t, Try<T>>> which holds the index of the first completed Future along with its result. The input futures are moved in, so your copies are no longer valid. If multiple input futures complete "simultaneously" or are already completed, a winner is chosen but the choice is undefined. For example:

    + +
    collectAny(fs, [](const std::pair<size_t, Try<int>>& p){
    +  size_t index = p.first;
    +  int result = p.second.value();
    +  // ...
    +});
    + +

    map() #

    + +

    map() is the Futures equivalent of the higher order function map. It takes a collection of Future<A> (or a pair of iterators thereof) and a function that can be passed to Future<A>::then(), and in turn calls then() with the function on each input Future. It returns a vector of the resultant Futures in the order they were passed in. This is simple sugar for:

    + +
    std::vector<Future<A>> fs;
    +std::vector<Future<B>> fs2;
    +for (auto it = fs.begin(); it < fs.end(); it++) {
    +  fs2.push_back(it->then(func));
    +}
    + +

    For instance, say you have some expensive RPC that fetches an int and you'd like to do expensive processing on each of many calls to this RPC. collect() or collectAll() might not be wise since they wait for all the results to be ready, while you'd rather process the integers as they arrive. You could use map() in this scenario:

    + +
    auto fs2 = map(fs, expensiveProcessingFunc);
    +// You probably now want to wait for all of these to complete. Call
    +// collect() or collectAll() on fs2 to obtain such a Future.
    + +

    reduce() #

    + +

    reduce() is the Futures equivalent of the higher order function fold (foldl, specifically). It takes a collection of Future<A> (or a pair of iterators thereof), an initial value of type B, and a function taking two arguments - the reduced value of type B and the next result from the collection of Future<A>. The function must return either B or Future<B>. reduce(), in turn, returns a Future<B>. The function will be applied to the initial value and the result of the first Future, and then to the result of that initial application and the result of the second Future, and so on until the whole collection of Futures has been reduced or an unhandled exception is hit.

    + +

    The second argument to the reducing function can be either A or Try<A>, depending on whether you want to handle exceptions from the input Futures. If there is an exception in an input Future and you don't take a Try, the reduce operation will short circuit with that exception. Any exception thrown in the reducing function will similarly short circuit the whole operation.

    + +

    For instance, if you have a collection of Future<int> and you want a Future<bool> that contains true if and only if all the input ints are equal to zero, you might write:

    + +
    reduce(fs, true, [](bool b, int i){
    +  // You could also return a Future<bool> if you needed to
    +  return b && (i == 0); 
    +})
    +.then([](bool result){
    +  // result is true if all inputs were zero
    +});
    +// You could use onError or Try here in case one of your input Futures
    +// contained an exception or if your reducing function threw an exception 
    + +

    To demonstrate the exception handling case, suppose you have a collection of Future<T> and you want a Future<bool> that contains true if all the input Futures are non-exceptional:

    + +
    reduce(fs, true, [](bool b, Try<T> t){
    +  return b && t.hasValue();
    +})
    +.then([](bool result){
    +  // result is true if all inputs were non-exceptional
    +});
    + +

    And finally one example where we're not reducing to a bool - here's how you might calculate the sum of a collection of Future<int>:

    + +
    reduce(fs, 0, [](int a, int b){
    +  return a + b;
    +})
    +.then([](int sum){
    +  // ...
    +});
    + +

    See the reduce() tests in the Future tests for a more complete catalog of possibilities.

    + +

    unorderedReduce() #

    + +

    Like reduce(), but consumes Futures in the collection as soon as they become ready. Use this if your function doesn't depend on the order of the Futures in the input collection. See the tests for examples.

    + +

    window() #

    + +

    window() is a sliding window implementation for Futures. It takes a collection of T (or a pair of iterators thereof), a function taking a T&& and returning a Future<S>, and a window size n. window() will create up to n Futures at a time using the function. As Futures complete, new Futures are created until the collection is exhausted.

    + +

    It ensures that at any given time, no more than n Futures are being processed.

    + +

    Combine with collectAll, reduce or unorderedReduce. See the tests for examples.

    + +

    Other Possibilities #

    + +

    There are a number of other possibilities for composing multiple Futures which we'll probably get around to at some point. If any of these seem like they would come in handy, let us know or better yet submit a diff:

    + +
      +
    • filter()
    • +
    • <your suggestion here>
    • +

    Multithreading and via()

    What to know and what to watch out for when using futures in a multithreaded environment, and how to control your threading model.

    Futures are thread safe... with a catch. #

    + +

    The core mutating operations on Futures and Promises are thread safe, insofar as they will throw exceptions if misused (usually, this means being called more than once, including simultaneous calls from different threads). For example, then(), onError() and other methods that set callbacks on Futures will throw exceptions if called twice. The same goes for fulfilling Promises via setValue() and setException().

    + +

    So what's the catch? Let's look at the following example of multithreaded Futures code:

    + +
    // Thread A
    +Promise<void> p;
    +auto f = p.getFuture();
    +
    +// Thread B
    +f.then(x).then(y);
    +
    +// Thread A
    +p.setValue();
    + +

    In which thread are x and y executed? Unfortunately, it depends. There is a race between setting the callbacks and fulfilling the promise. If setting the callbacks wins, they will be executed in thread A when the Promise is fulfilled. If setting the value wins, they will be executed in thread B as soon as they are set. If setValue() sneaks in at just the right time between the two then() calls, then x will be executed in thread A and y will be executed in thread B. You could imagine that this nondeterminism might become unwieldy or downright unacceptable. Thankfully, there's a mechanism to resolve this race and give you fine-grained control over your execution model.

    + +

    via() to the rescue #

    + +

    Futures have a method called via() which takes an Executor. Executor is a simple interface that requires only the existence of an add(std::function<void()> func) method which must be thread safe and must execute the provided function somehow, though not necessarily immediately. via() guarantees that a callback set on the Future will be executed on the given Executor. For instance:

    + +
    makeFuture()
    +  .then(x)
    +  .via(exe1).then(y)
    +  .via(exe2).then(z);
    + +

    In this example, y will be executed on exe1, and z will be executed on exe2. This is a fairly powerful abstraction. It not only solves the above race, but gives you clear, concise, and self-documenting control over your execution model. One common pattern is having different executors for different types of work (e.g. an IO-bound pool spinning on event bases doing your network IO and a CPU-bound thread pool for expensive work) and switching between them with via().

    + +

    There is also a static function via() that creates a completed Future<void> that is already set up to call back on the provided Executor.

    + +

    Or, pass an Executor to then() #

    + +

    An alternative to via() is to pass an Executor as the first parameter to then(), which causes the callback to be executed via that Executor. Unlike via() the Executor is not sticky, it only applies for this callback. See the docblock for more details and caveats.

    + +

    Executor implementations #

    + +

    via() wouldn't be of much use without practical implementations around. We have a handful, and here's a (possibly incomplete) list.

    + +
      +
    • ThreadPoolExecutor is an abstract thread pool implementation that supports resizing, custom thread factories, pool and per-task stats, NUMA awareness, user-defined task expiration, and Codel task expiration. It and its subclasses are under active development. It currently has two implementations:
    • +
    • folly's EventBase is an Executor and executes work as a callback in the event loop
    • +
    • ManualExecutor only executes work when manually cranked. This is useful for testing.
    • +
    • InlineExecutor executes work immediately inline
    • +
    • QueuedImmediateExecutor is similar to InlineExecutor, but work added during callback execution will be queued instead of immediately executed
    • +
    • ScheduledExecutor is a subinterface of Executor that supports scheduled (i.e. delayed) execution. There aren't many implementations yet, see T5924392
    • +
    • Thrift's ThreadManager is an Executor but we aim to deprecate it in favor of the aforementioned CPUThreadPoolExecutor
    • +
    • FutureExecutor wraps another Executor and provides Future<T> addFuture(F func) which returns a Future representing the result of func. This is equivalent to via(executor).then(func) and the latter should probably be preferred.
    • +

    Timeouts and related features

    Futures provide a number of timing-related features. Here's an overview.

    Timing implementation #

    + +

    Timing resolution #

    + +

    The functions and methods documented below all take a Duration, which is an alias for std::chrono::milliseconds. Why not allow more granularity? Simply put, we can't guarantee sub-millisecond resolution and we don't want to lie to you.

    + +

    The TimeKeeper interface #

    + +

    Most timing-related methods also optionally take a default implementation uses a folly::HHWheelTimer in a dedicated EventBase thread to manage timeouts.

    + +

    within() #

    + +

    Future<T>::within() returns a new Future that will complete with the provided exception (by default, a TimedOut exception) if it does not complete within the specified duration. For example:

    + +
    Future<int> foo();
    +
    +// f will complete with a TimedOut exception if the Future returned by foo()
    +// does not complete within 500 ms
    +f = foo().within(Duration(500));
    +
    +// Same deal, but a timeout will trigger the provided exception instead
    +f2 = foo().within(Duration(500), std::runtime_error("you took too long!"));
    + +

    onTimeout() #

    + +

    Future<T>::onTimeout() lets you simultaneously set up a timeout and a timeout handler. For example:

    + +
    Future<int> foo();
    +foo()
    +  .onTimeout(Duration(500), []{
    +    // You must maintain the resultant future's type
    +    // ... handle timeout ...
    +    return -1;
    +  })
    +  .then(...);
    + +

    The astute reader might notice that this is effectively syntactic sugar for

    + +
    foo()
    +  .within(Duration(500))
    +  .onError([](const TimedOut& e) {
    +    // handle timeout
    +    return -1;
    +  })
    +  .then(...);
    + +

    get() and wait() with timeouts #

    + +

    get() and wait(), which are detailed in the Testing article, optionally take timeouts:

    + +
    Future<int> foo();
    +// Will throw TimedOut if the Future doesn't complete within one second of
    +// the get() call
    +int result = foo().get(Duration(1000));
    +
    +// If the Future doesn't complete within one second, f will remain
    +// incomplete. That is, if a timeout occurs, it's as if wait() was
    +// never called.
    +Future<int> f = foo().wait(Duration(1000));
    + +

    delayed() #

    + +

    Future<T>::delayed() returns a new Future whose completion is delayed for at least the specified duration. For example:

    + +
    makeFuture()
    +  .delayed(Duration(1000))
    +  .then([]{
    +    // This will be executed when the original Future has completed or when
    +    // 1000ms has elapsed, whichever comes last.
    +  });
    + +

    futures::sleep() #

    + +

    sleep() returns a void Future that will complete after the specified duration. For example:

    + +
    futures::sleep(Duration(1000)).then([]{
    +  // This will be executed after 1000ms
    +});

    Interrupts and Cancellations

    Interrupts are a mechanism for Future holders to send a signal to Promise holders. Here's how to use them.

    Let's say that your Futures code kicks off some long, expensive operation in another thread. A short while later, something comes up that obviates the need for the result of that operation. Are those resources gone forever? Not necessarily. Enter interrupts.

    + +

    Interrupts allow Future holders to send signals in the form of exceptions to Promise holders, who are free to handle the interrupt as they please (or not at all). For example:

    + +
    Promise<int> p;
    +p.setInterruptHandler([](const exception_wrapper& e){
    +  // Handle the interrupt. For instance, we could just fulfill the Promise
    +  // with the given exception:
    +  p.setException(e);
       
    -```
    -making Promise
    -Future chain made
    -fulfilling Promise
    -foo(42)
    -Promise fulfilled
    -```
    +  // Or maybe we want the Future to complete with some special value
    +  p.setValue(42);
    +
    +  // Or maybe we don't want to do anything at all! Including not setting
    +  // this handler in the first place.
    +});
    +
    +auto f = p.getFuture();
    +// The Future holder can now send an interrupt whenever it wants via raise().
    +// If the interrupt beats out the fulfillment of the Promise *and* there is
    +// an interrupt handler set on the Promise, that handler will be called with
    +// the provided exception
    +f.raise(std::runtime_error("Something went awry! Abort!"));
    +
    +// cancel() is syntactic sugar for raise(FutureCancellation())
    +f.cancel();
    + +

    Going forward, we'd like to integrate interrupts with major Future interface provides as a way to cancel RPCs and the like, but that's not in place yet. This is a bleeding edge feature—please let us know your use cases so that we can iterate!

    Testing

    Testing futures-based code does not have to be a pain. Here are some tips and idiomatic approaches.

    Extracting values synchronously #

    + +
    NOTE: The tests in this article are written using the gtest framework.
    + +

    Let's say we want to test the following interface:

    + +
    Future<bool> isPrime(int n);
    + +

    We could make a couple of calls and set expectations on the resultant futures via value():

    + +
    EXPECT_TRUE(isPrime(7).value());
    +EXPECT_FALSE(isPrime(8).value());
    + +

    But what if isPrime() is asynchronous (e.g. makes an async call to another service that computes primeness)? It's now likely that you'll call value() before the Future is complete, which will throw a FutureNotReady exception.

    + +

    A naive approach is to spin until the Future is complete:

    + +
    // Spin until ready. Gross. Don't do this.
    +auto f = isPrime(7);
    +while (!f.isReady()) {}
    +EXPECT_TRUE(f.value());
    + +

    Thankfully, we have some better options in the form of Future<T>::get() and Future<T>::wait().

    + +

    get() #

    + +

    T Future<T>::get() blocks until the Future is complete and either returns a moved out copy of the value or throws any contained exception. You can use it like so.

    + +
    EXPECT_TRUE(isPrime(7).get());
    + +

    Keep in mind that some other thread had better complete the Future, because the thread that calls get() will block. Also, get() optionally takes a timeout after which its throws a TimedOut exception. See the Timeouts article for more information.

    -## User Guide +

    wait() #

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

    Future<T> Future<T>::wait() is similar to get() in that it blocks until the Future is complete. However, instead of returning a value or throwing an exception, it returns a new completed Future with the result of the original Future. One use case is when you'd rather not have the throwing behavior of get() so that you can check for exceptions separately without a try/catch. For example:

    - Result result; - // The value when result is FOUND, - // The error message when result is SERVER_ERROR or CLIENT_ERROR - // undefined otherwise - std::string value; - }; +
    auto f = isPrime(7).wait();
    +EXPECT_FALSE(f.getTry().hasException());
    +EXPECT_TRUE(f.value());
    - GetReply get(std::string key); -}; -``` +

    Like get(), wait() optionally takes a timeout. Again, see the Timeouts article.

    -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. +

    getVia() and waitVia() #

    -Now, consider this traditional asynchronous signature for `get()`: +

    T Future<T>::getVia(DrivableExecutor*) and Future<T> Future<T>::waitVia(DrivableExecutor*) have the same semantics as get() and wait() except that they drive some Executor until the Future is complete. ManualExecutor. These are simple but useful sugar for the following common pattern:

    -```C++ -int get(std::string key, std::function callback); -``` +

    Given this:

    -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". +
    auto f = doAsyncWorkOnEventBase(&eventBase);
    -The Future-based API looks like this: +

    Don't do this:

    -```C++ -Future get(std::string key); -``` +
    while (!f.isReady()) {
    +  eb.loop();
    +}
    -A `Future` is a placeholder for the `GetReply` that we will eventually get. A Future usually starts life out "unfulfilled", or incomplete, i.e.: +

    Do one of these instead:

    -```C++ -fut.isReady() == false -fut.value() // will throw an exception because the Future is not ready -``` +
    auto val = f.getVia(&eventBase);
    +// or
    +f.waitVia(&eb).then([](Value val){ ... });
    -At some point in the future, the Future will have been fulfilled, and we can access its value. +

    Using gmock #

    -```C++ -fut.isReady() == true -GetReply& reply = fut.value(); -``` +

    Google Mock is a powerful mocking framework for writing and using C++ mock classes. Unfortunately, Gmock requires that the parameters and return types of mocked functions and methods are copyable. You're likely to hit this issue when mocking Futures code because Futures (and, less importantly, Promises) are noncopyable, and many of your interfaces will return Futures and some will even be passed Futures.

    -Futures support exceptions. If something exceptional happened, your Future may represent an exception instead of a value. In that case: +

    The canonical approach to mocking interfaces that involve noncopyable objects is to override your interface with a dummy method that simply calls a mock method that has had the noncopyable components stripped or replaced. For Futures, this usually means returning/passing contained values directly and synchronously, which shouldn't be a problem since your mocks won't actually be asynchronous. Here is a very contrived but demonstrative example:

    -```C++ -fut.isReady() == true -fut.value() // will rethrow the exception -``` +
    // The async interface we want to mock
    +class AsyncClient {
    + public:
    +  virtual Future<int> foo(int i);
    +};
     
    -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".
    +// The mock implementation
    +class MockAsyncClient : public AsyncClient {
    + public:
    +  // Declare a mock method foo_ that takes an int and returns an int
    +  MOCK_METHOD1(foo_, int(int));
     
    -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.
    +  // Plug the mock into an override of the interface
    +  Future<int> foo(int i) override {
    +    // Lift the result back into a Future before returning
    +    return makeFuture<int>(foo_(i));
    +  }
    +};
     
    -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.
    +// Let's say that we're testing a class MyProxy that simply forwards foo()
    +// calls to AsyncClient and returns the result
    +class MyProxy {
    + public:
    +  Future<int> foo(int i) {
    +    return client->foo(i);
    +  }
    +  void setClient(AsyncClient* client);
    + private:
    +  AsyncClient* client;
    +};
     
    -```C++
    -vector> futs;
    -for (auto& key : keys) {
    -  futs.push_back(mc.get(key));
    -}
    -auto all = collectAll(futs.begin(), futs.end());
    +// Now, in our testing code
    +MyProxy proxy;
    +MockAsyncClient mockClient;
    +// Inject the mock
    +proxy.setClient(&mockClient)
    +// Set an expectation on the mock to be called with 42 and return 84
    +EXPECT_CALL(mockClient, foo_(42)).WillOnce(Return(84));
    +// Trigger the call
    +auto f = MyProxy.foo(42);
    +// If everything has been mocked out synchronously, we can just check the
    +// value of the future directly
    +EXPECT_EQ(84, f.value());

    Pitfalls

    EventBase, EventBaseManager, Executor #

    -vector> futs; -for (auto& key : keys) { - futs.push_back(mc.get(key)); -} -auto any = collectAny(futs.begin(), futs.end()); -``` +

    It's not uncommon to hit a snag (especially when using via()) where you're hanging for (a) being on the wrong thread (b) talking to an EventBase which is not actually spinning (loopForever).

    -`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 `collectN()` for when you need *some*.) +

    Some tips:

    -Second, we can attach callbacks to a Future, and chain them together monadically. An example will clarify: +
      +
    • evb->isInRunningEventBase()
    • +
    • evb->isRunning()
    • +
    -```C++ -Future fut1 = mc.get("foo"); +

    Lambda Arguments #

    -Future fut2 = fut1.then( - [](Try&& t) { - if (t.value().result == MemcacheClient::GetReply::Result::FOUND) - return t.value().value; - throw SomeException("No value"); - }); +

    The danger with lambdas is you'll try to read a variable that's gone

    -Future fut3 = fut2.then( - [](Try&& t) { - try { - cout << t.value() << endl; - } catch (std::exception const& e) { - cerr << e.what() << endl; - } - }); -``` +
    Object obj = ...;
    +return future1.then([&] {
    +    // ..work..
    +    obj.method();
    +      // woops object is gone from the 
    +      // stack when this actually runs
    +});
    -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` is the object wrapper that supports both value and exception. +

    Sometimes it makes sense to copy inputs. Sometimes that's too expensive and a shared_ptr is best. Sometimes the nature of things lends itself to the contract "this won't go away" and you take a raw pointer, but this should only be used when it's a very natural fit. In particular, you don't want people wishing you took a shared pointer and having to do something like this to work around it:

    -Using `then` to add callbacks is idiomatic. It brings all the code into one place, which avoids callback hell. +
    auto foo = make_shared<Foo>();
    +yourFunction(foo.get(),
    +  [foo]{ 
    +     /* callback doesn't use foo, but captures the 
    +      * shared pointer to keep it alive 
    +      */
    +});
    -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. Futures have a blocking method called wait() that does exactly that and optionally takes a timeout. +

    In general: +prefer taking inputs by value if they're small enough +if inputs are big (measurably expensive to copy), then keep them on the heap and prefer a shared_ptr +if you are really sure you need to get more fancy, put on your wizard hat and go to it ;)

    + +
    Object obj = ...;
    +return future1.then([obj] {  // capture by value
    +    // ..work..
    +    obj.method();
    +      // works on copy of obj
    +});
    + +

    If Object is large:

    + +
    auto optr = makeShared<Object>(...);
    +return future1.then([optr] {  // copy ptr, use count = 2
    +    // ..work..
    +    optr->method();
    +      // works on original object
    +    // use-count for optr goes to 0 and object destructs
    +});

    Future as a Monad

    A semi-formal and totally optional analysis of Future as a monad.

    Future is a monad. You don't need to know this or what it means to use Futures, but if you are curious, want to understand monads better, or eat functional flakes for breakfast, then keep reading this extremely extracurricular document.

    + +

    Let's review the definition of a monad. Formal definitions are mathematical and/or in Haskellese and therefore opaque to imperative mortals. But here's a simplified description using a subset of Haskell type notation that is useful but not confusing:

    + +
    -- "unit" is a function that takes a value and wraps it in the monad type.
    +-- Haskellers call this "return" as some kind of sick inside joke.
    +unit :: a -> m a
    +
    +-- "bind" is a function that takes a monad, and a function that takes a value
    +-- and returns another monad. Haskellers call this ">>=" because they are
    +-- vying to unseat perl from the throne of illegibility.
    +bind :: m a -> (a -> m b) -> m b
    + +

    Monads must also satisfy these three axioms:

    + +
    -- Left Identity
    +unit a `bind` f ≡ f a
    +-- Right Identity
    +m `bind` unit ≡ m
    +-- Associativity
    +(m `bind` f) `bind` g ≡ m `bind` (\x -> f x `bind` g)
    -Futures are 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. +

    I won't try to explain that, there are many blog posts and wiki pages that try to do that. Instead, I'll substitute the equivalent Future monad expressions, and the whole thing will (probably) start to make sense. First, a simplified Future type:

    -```C++ -// Thread A -Promise p; -auto f = p.getFuture(); +
    template <class A>
    +struct Future {
    +  // The constructor that takes a value is "unit"
    +  Future(A);
     
    -// Thread B
    -f.then(x).then(y).then(z);
    +  // "then" is "bind"
    +  template <class B>
    +  Future<B> then(std::function<Future<B>(A));
     
    -// 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.
    +// "makeFuture" is also "unit", and we will need it because constructors can't
    +// really be converted to std::function (AFAIK)
    +template <class A>
    +Future<A> makeFuture(A);
    -Naturally, you will want some control over which thread executes callbacks. We have a few mechanisms to help. +

    Now, the three axioms (Futures don't define operator== but you get the idea):

    -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. +
    // Left Identity
    +A a;
    +Future<A>(a).then(f) == f(a)
     
    -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).
    +// Right Identity
    +Future<A> m;
    +m.then(makeFuture<A>) == m
     
    -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
    -```
    +// Associativity
    +Future<A> m;
    +std::function<Future<B>(A)> f;
    +std::function<Future<C>(B)> g;
    +m.then(f).then(g) == m.then([](A x) { return f(x).then(g); })
    -## You make me Promises, Promises +

    So, in plain english this says a monad like Future has a way to get stuff in the monad (unit/makeFuture), and a way to chain things together (bind/then). unit semantics are unsurprising, and chaining is the same as nesting. Something that behaves this way is a monad, and Future is a monad.

    -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 fulfill it with a value or an exception. Example: +
    Remember how Futures do more than just hold values? The nature of the underlying asynchronous operations (usually I/O) generally includes side effects, and this breaks our pure formalism. You may or may not be able to make your async operations (observable) side-effect free, but you can make your intermediate Future callbacks functionally pure (aka value semantics), and if you do you will be happier than if you mutate state. But I won't beat that dead horse here—I know you will probably mutate state anyway because you're a perf-conscious C++ developer and speed trumps safety. But do try to minimize it.
    -```C++ -Promise p; -Future f = p.getFuture(); +

    Ok, so now we know Future is a monad. What can we do with this newfound power? Knowledge is power, right? Well, you can brag to your friends, for one thing. C++ doesn't really provide any concrete reusable tools for things that are monads. There's no do-blocks, or some generic monad-aware functional toolkit that includes map, filter, fold, etc. But what you do get is a way of thinking about and reasoning about your Futures that transcends our own little implementation, and doesn't require that you grok all the opaque internals of the implementation to do it.

    -f.isReady() == false +

    But mostly it makes you cool.

    -p.setValue(42); +

    Kleisli Composition (extra extra credit) #

    -f.isReady() == true -f.value() == 42 -``` +

    If "associative" doesn't look associative to you, then you are very astute. Congratulations! You win a maths unicorn. +The three laws refer to a different formulation of the axioms, in terms of the Kleisli Composition operator (>=>), which basically says compose two monad-making functions in the obvious way.

    -and an exception example: +
    (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
     
    -```C++
    -Promise p;
    -Future f = p.getFuture();
    +-- Left Identity
    +unit >=> g ≡ g
    +-- Right Identity
    +f >=> unit ≡ f
    +-- Associativity
    +(f >=> g) >=> h ≡ f >=> (g >=> h)
    -f.isReady() == false +

    We accidentally implemented this operator, and called it chain. Then we removed it in favor of Future::thenMulti. But it totally existed, so use your imagination:

    -p.setException(std::runtime_error("Fail")); +
    // Left Identity
    +chain(makeFuture, g) ≡ g
    +// Right Identity
    +chain(f, makeFuture) ≡ f
    +// Associativity
    +chain(chain(f, g), h) ≡ chain(f, chain(g, h)) // and chain(f, g, h)
    -f.isReady() == true -f.value() // throws the exception -``` +

    Further reading #

    -It's good practice to use setWith which takes a function and automatically captures exceptions, e.g. +

    FAQ

    Why not use std::future? #

    -```C++ -Promise p; -p.setWith([]{ - 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 -}); -``` +

    No callback support. See also http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3428.pdf

    -## FAQ +

    Why not use boost::future? #

    -### Why not use std::future? -No callback support. -See also http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3428.pdf +
      +
    • At the time of writing, 1.53 (the first version with the requisite features) was brand new, not well-tested, and not available to Facebook developers.
    • +
    • It is 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 executor support was nonexistent (and now, is still experimental).
    • +
    -### 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 (like Twitter's)? #

    -### 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. +

    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. +

    What about proper continuations (fibers)? Futures suck. #

    -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)). +

    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.

    -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. +

    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 and [[folly/experimental/fibers| +https://github.com/facebook/folly/tree/master/folly/experimental/fibers]]).

    -Futures 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. +

    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, the code might read cleaner, and debugging stack traces might be much easier.

    -## Additional Documentation -[This documentation](DEX.md) is more in-depth, generated from Facebook internal documentation of Futures. We are in the process of merging our Open Source documentation (this file and others) with that. +

    Futures 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.

    -- 2.34.1