You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by be...@apache.org on 2017/11/25 20:07:13 UTC

mesos git commit: Improved libprocess documentation.

Repository: mesos
Updated Branches:
  refs/heads/master 340687ea4 -> def46583c


Improved libprocess documentation.

* Reworked overview and table of contents.
* Improved Futures and Promises section.
* Added documentation for abandonment and `Future::recover()`.
* Improved section on processes (still more to do).


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/def46583
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/def46583
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/def46583

Branch: refs/heads/master
Commit: def46583caeb664cc300e9745ea549968c5ae3ab
Parents: 340687e
Author: Benjamin Hindman <be...@gmail.com>
Authored: Fri Nov 24 13:37:18 2017 -0800
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Sat Nov 25 12:06:48 2017 -0800

----------------------------------------------------------------------
 3rdparty/libprocess/README.md | 842 ++++++++++++++++++++-----------------
 1 file changed, 446 insertions(+), 396 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/def46583/3rdparty/libprocess/README.md
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/README.md b/3rdparty/libprocess/README.md
index 05d5896..56c9ae6 100644
--- a/3rdparty/libprocess/README.md
+++ b/3rdparty/libprocess/README.md
@@ -1,546 +1,369 @@
-# Libprocess Developer Guide
+# Libprocess User Guide
 
-*Note:* this Developer Guide is Work in Progress.
+libprocess provides general primitives and abstractions for asynchronous programming with [futures/promises](https://en.wikipedia.org/wiki/Futures_and_promises), [HTTP](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol), and [actors](http://en.wikipedia.org/wiki/Actor_model).
 
-The library _libprocess_ provides high level elements for an
-actor programming style with asynchronous message-handling and a
-variety of related basic system primitives. Its API and
-implementation are written in C++.
+> <br> **Inspired by [Erlang](http://erlang.org), libprocess gets it's name from calling an "actor" a "process" (not to be confused by an operating system process).** <br><br>
 
+## Table of Contents
 
-## Introduction
-
-The design of libprocess is inspired by [Erlang](http://erlang.org),
-a language that implements the
-[actor model](http://en.wikipedia.org/wiki/Actor_model).
-
-As the name already suggests, one of the libprocess core concepts is a
-[Process](#process). This is a single threaded, independent actor which
-communicates with other processes, locally and remotely, by sending and receiving [HTTP requests and responses](#http).
-
-At a higher level, functional composition of processes is facilitated using [futures and promises](#futures-and-promises).
-
-
-## Overview
-
-### Table of Contents
-
-* [Processes and the Asynchronous Pimpl Pattern](#processes)
+* [Overview](#overview)
 * [Futures and Promises](#futures-and-promises)
 * [HTTP](#http)
+* [Processes (aka Actors)](#processes)
 * [Clock Management and Timeouts](#clock)
 * [Miscellaneous Primitives](#miscellaneous-primitives)
 * [Optimized Run Queue and Event Queue](#optimized-run-queue-event-queue)
 
 ---
 
-## <a name="processes"></a> Processes and the Asynchronous Pimpl Pattern
+## <a name="overview"></a> Overview
 
-A `process` is an actor, effectively a cross between a thread and an object.
+This user guide is meant to help understand the constructs within the libprocess library. The main constructs are:
 
-Creating/spawning a process is very cheap (no actual thread gets
-created, and no thread stack gets allocated).
+1. [Futures and Promises](#futures-and-promises) which are used to build ...
+2. [HTTP](#http) abstractions, which make the foundation for ...
+3. [Processes (aka Actors)](#processes).
 
-Each process has a queue of incoming events that it processes one at a
-time.
+For most people processes (aka actors) are the most foreign of the concepts, but they are arguably the most critical part of the library (they library is named after them!). Nevertheless, we organized this guide to walk through futures/promises and HTTP before processes because the former two are prerequisites for the latter.
 
-Processes provide execution contexts (only one thread executing within
-a process at a time so no need for per process synchronization).
+## <a name="futures-and-promises"></a> Futures and Promises
 
-<!---
-```cpp
-using namespace process;
+The `Future` and `Promise` primitives are used to enable programmers to write asynchronous, non-blocking, and highly concurrent software.
 
-class MyProcess : public Process<MyProcess> {};
+A `Future` acts as the read-side of a result which might be computed asynchronously. A `Promise`, on the other hand, acts as the write-side "container".
 
-int main(int argc, char** argv)
-{
-  MyProcess process;
-  spawn(process);
-  terminate(process);
-  wait(process);
-  return 0;
-}
-```
----->
+Looking for a specific topic?
 
-### `delay`
+* [Basics](#futures-and-promises-basics)
+* [States](#futures-and-promises-states)
+* [Disarding a Future (aka Cancellation)](#futures-and-discarding-a-future)
+* [Abandoned Futures](#futures-and-abandoned-futures)
+* [Composition: `Future::then()`, `Future::repair()`, and `Future::recover()`](#futures-and-promises-composition)
+* [Discarding and Composition](#futures-and-promises-discarding-and-composition)
+* [Callback Semantics](#futures-and-promises-callback-semantics)
+* [`CHECK()` Overloads](#futures-and-promises-check-overloads)
 
-`delay` instead of [dispatching](#dispatch) for execution right away, it allows it to be scheduled after a certain time duration.
+### <a name="futures-and-promises-basics"></a> Basics
 
+A `Promise` is templated by the type that it will "contain". A `Promise` is not copyable or assignable in order to encourage strict ownership rules between processes (i.e., it's hard to reason about multiple actors concurrently trying to complete a `Promise`, even if it's safe to do so concurrently).
 
-### `dispatch`
+You can get a `Future` from a `Promise` using `Promise::future()`. Unlike `Promise`, a `Future` can be both copied and assigned.
 
-`dispatch` schedules a method for asynchronous execution.
+> <br> As of this time, the templated type of the future must be the exact same as the promise: you cannot create a covariant or contravariant future. <br><br>
+
+Here is a simple example of using `Promise` and `Future`:
 
-<!---
 ```cpp
-using namespace process;
+using process::Future;
+using process::Promise;
 
-class QueueProcess : public Process<QueueProcess>
+int main(int argc, char** argv)
 {
-public:
-  void enqueue(int i) { this->i = i; }
-  int dequeue() { return this->i; }
-
-private:
-  int i;
-};
+  Promise<int> promise;
 
+  Future<int> future = promise.future();
 
-int main(int argc, char** argv)
-{
-  QueueProcess process;
-  spawn(process);
+  // You can copy a future.
+  Future<int> future2 = future;
 
-  dispatch(process, &QueueProcess::enqueue, 42);
-  dispatch(process, &QueueProcess::enqueue, 43);
+  // You can also assign a future (NOTE: this future will never
+  // complete because the Promise goes out of scope, but the
+  // Future is still valid and can be used normally.)
+  future = Promise<int>().future();
 
-  ...;
+  return 0;
 }
 ```
----->
-
-
-### `defer`
 
-Objects like `Future` allow attaching callbacks that get executed
-_synchronously_ on certain events, such as the completion of a future
-(e.g., `Future::then` and `Future::onReady`) or the failure of a
-future (e.g., `Future::onFailed`). It's usually desireable, however, to
-execute these callbacks _asynchronously_, and `defer` provides a mechanism
-to do so.
+### <a name="futures-and-promises-states"></a> States
 
-`defer` is similar to [`dispatch`](#dispatch), but rather than
-enqueing the execution of a method or function on the specified
-process immediately (i.e., synchronously), it returns a `Deferred`,
-which is a callable object that only after getting _invoked_ will
-dispatch the method or function on the specified process. Said another
-way, using `defer` is a way to _defer_ a `dispatch`.
+A promise starts in the `PENDING` state and can then transition to any of the `READY`, `FAILED`, or `DISCARDED` states. You can check the state using `Future::isPending()`, `Future::isReady()`, `Future::isFailed()`, and `Future::isDiscarded()`.
 
-As an example, consider the following function, which spawns a process
-and registers two callbacks on it, one using `defer` and another
-without it:
+> <br> We typically refer to transitioning to `READY` as _completing the promise/future_. <br><br>
 
-```cpp
-using namespace process;
-
-void foo()
-{
-  ProcessBase process;
-  spawn(process);
+You can also add a callback to be invoked when (or if) a transition occurs (or has occcured) by using the `Future::onReady()`, `Future::onFailed()`, and `Future::onDiscarded()`. As a _catch all_ you can use `Future::onAny()` which will invoke it's callbacks on a transition to all of `READY`, `FAILED`, and `DISCARDED`. **See [Callback Semantics](#futures-and-promises-callback-semantics) for a discussion of how/when these callbacks get invoked.**
 
-  Deferred<void(int)> deferred = defer(
-      process,
-      [](int i) {
-        // Invoked _asynchronously_ using `process` as the
-        // execution context.
-      });
+The following table is meant to capture these transitions:
 
-  Promise<int> promise;
+| Transition | `Promise::*()` | `Future::is*()` | `Future::on*()` |
+| ------ | -------------- | --------------- | --------------- |
+| `READY` | `Promise::set(T)` | `Future::isReady()` | `Future::onReady(F&&)` |
+| `FAILED` | `Promise::fail(const std::string&)` | `Future::isFailed()` | `Future::onFailed(F&&)` |
+| `DISCARDED` | `Promise::discard()` | `Future::isDiscarded()` | `Future::onDiscarded(F&&)` |
 
-  promise.future().then(deferred);
+> <br> Code Style: prefer [composition](#futures-and-promises-composition) using `Future::then()` and `Future::recover()` over `Future::onReady()`, `Future::onFailed()`, `Future::onDiscarded()`, and `Future::onAny()`. A good rule of thumb is if you find yourself creating your own instance of a `Promise` to compose an asynchronous operation you should use [composition](#futures-and-promises-composition) instead! <br><br>
 
-  promise.future().then([](int i) {
-    // Invoked synchronously from the execution context of
-    // the thread that completes the future!
-  });
+We use the macros `CHECK_PENDING()`, `CHECK_READY()`, `CHECK_FAILED()`, `CHECK_DISCARDED()` throughout our examples. See [`CHECK()` Overloads](#futures-and-promises-check-overloads) for more details about these macros.
 
-  // Executes both callbacks synchronously, which _dispatches_
-  // the deferred lambda to run asynchronously in the execution
-  // context of `process` but invokes the other lambda immediately.
-  promise.set(42);
-
-  terminate(process);
-}
-```
+### <a name="futures-and-promises-discarding-a-future"></a> Disarding a Future (aka Cancellation)
 
-As another example, consider this excerpt from the Mesos project's
-`src/master/master.cpp`:
+You can "cancel" the result of some asynchronous operation by discarding a future. Unlike doing a discard on a promise, _discarding a future is a request that may or may not be be satisfiable_. You discard a future using `Future::discard()`. You can determine if a future has a discard request by using `Future::hasDiscard()` or set up a callback using `Future::onDiscard()`. Here's an example:
 
 ```cpp
-// Start contending to be a leading master and detecting the current leader.
-// NOTE: `.onAny` passes the relevant future to its callback as a parameter, and
-// `lambda::_1` facilitates this when using `defer`.
-contender->contend()
-  .onAny(defer(self(), &Master::contended, lambda::_1));
-```
+using process::Future;
+using process::Promise;
 
-Why use `defer` in this context rather than just executing
-`Master::detected` synchronously? To answer this, we need to remember
-that when the promise associated with the future returned from
-`contender->contend()` is completed that will synchronously invoke all
-registered callbacks (i.e., the `Future::onAny` one in the example
-above), _which may be in a different process_! Without using `defer`
-the process responsible for executing `contender->contend()` will
-potentially cause `&Master::contended` to get executed simultaneously
-(i.e., on a different thread) than the `Master` process! This creates
-the potential for a data race in which two threads access members of
-`Master` concurrently. Instead, using `defer` (with `self()`) will
-dispatch the method _back_ to the `Master` process to be executed at a
-later point in time within the single-threaded execution context of
-the `Master`. Using `defer` here precisely allows us to capture these
-semantics.
-
-A natural question that folks often ask is whether or not we ever
-_don't_ want to use `defer(self(), ...)`, or even just 'defer`. In
-some circumstances, you actually don't need to defer back to your own
-process, but you often want to defer. A good example of that is
-handling HTTP requests. Consider this example:
+int main(int argc, char** argv)
+{
+  Promise<int> promise;
 
-```cpp
-using namespace process;
+  Future<int> future = promise.future();
 
-using std::string;
+  CHECK_PENDING(future);
 
-class HttpProcess : public Process<HttpProcess>
-{
-public:
-  virtual void initialize()
-  {
-    route("/route", None(), [](const http::Request& request) {
-      return functionWhichReturnsAFutureOfString()
-        .then(defer(self(), [](const string& s) {
-          return http::OK("String returned in body: " + s);
-        }));
-    });
-  }
-};
-```
+  future.discard();
 
-Now, while this is totally legal and correct code, the callback
-executed after `functionWhichReturnsAFutureOfString` is completed
-_does not_ need to be executed within the execution context of
-`HttpProcess` because it doesn't require any state from `HttpProcess`!
-In this case, rather than forcing the execution of the callback within
-the execution context of `HttpProcess`, which will block other
-callbacks _that must_ be executed by `HttpProcess`, we can simply just
-run this lambda using an execution context that libprocess can pick
-for us (from a pool of threads). We do so by removing `self()` as the
-first argument to `defer`:
+  CHECK(promise.future().hasDiscard());
 
-```cpp
-using namespace process;
+  CHECK_PENDING(future); // THE FUTURE IS STILL PENDING!
 
-using std::string;
-
-class HttpProcess : public Process<HttpProcess>
-{
-public:
-  virtual void initialize()
-  {
-    route("/route", None(), [](const http::Request& request) {
-      return functionWhichReturnsAFutureOfString()
-        .then(defer([](const string& s) {
-          return http::OK("String returned in body: " + s);
-        }));
-    });
-  }
-};
+  return 0;
+}
 ```
 
-_Note that even in this example_ we still want to use `defer`! Why?
-Because otherwise we are blocking the execution context (i.e.,
-process, thread, etc) that is _completing_ the future because it is
-synchronously executing the callbacks! Instead, we want to let the
-callback get executed asynchronously by using `defer.`
-
-Let's construct a simple example that illustrates a problem that can
-be introduced by omitting `defer` from callback registrations. We'll
-define a method for our `HttpProcess` class which accepts an input
-string and returns a future via an asyncronous method called
-`asyncStoreData`:
+The provider of the future will often use `Future::onDiscard()` to watch for discard requests and try and act accordingly, for example:
 
 ```cpp
-using namespace process;
-
-using std::string;
+using process::Future;
+using process::Promise;
 
-class HttpProcess : public Process<HttpProcess>
+int main(int argc, char** argv)
 {
-public:
-  // Returns the number of bytes stored.
-  Future<int> inputHandler(const string input);
-
-protected:
-  // Returns the number of bytes stored.
-  Future<int> asyncStoreData(const string input);
+  Promise<int> promise;
 
-  int storedCount;
-}
+  // Set up a callback to discard the future if
+  // requested (this is not always possible!).
+  promise.future().onDiscard([&]() {
+    promise.discard();
+  });
 
+  Future<int> future = promise.future();
 
-Future<int> HttpProcess::inputHandler(const string input)
-{
-  LOG(INFO) << "HttpProcess received input: " << input;
+  CHECK_PENDING(future);
 
-  return asyncStoreData(input)
-    .then([this](int bytes) -> Future<int> {
-      this->storedCount += bytes;
+  future.discard();
 
-      LOG(INFO) << "Successfully stored input. "
-                << "Total bytes stored so far: " << this->storedCount;
+  CHECK_DISCARDED(future); // NO LONGER PENDING!
 
-      return bytes;
-    });
+  return 0;
 }
 ```
 
-When the callback is registered on the `Future<int>` returned by
-`asyncStoreData`, a lambda is passed directly to `then`. This means
-that the lambda will be executed in whatever execution context
-eventually fulfills the future. If the future is fulfilled in a
-different execution context (i.e., inside a different libprocess
-process), then it's possible that the instance of `HttpProcess` that
-originally invoked `inputHandler` will have been destroyed, making
-`this` a dangling pointer. In order to avoid this possibility, the
-callback should be registered as follows:
-
-```cpp
-  return asyncStoreData(input)
-    .then(defer(self(), [this](int bytes) -> Future<int> {
-      ...
-    }));
-```
+### <a name="futures-and-promises-abandoned-futures"></a> Abandoned Futures
 
-The lambda is then guaranteed to execute within the execution context
-of the current process, and we know that `this` will still be a valid
-pointer. We should write libprocess code that makes no assumptions
-about the execution context in which a given future is fulfilled. Even
-if we can verify that a future will be fulfilled in the current
-process, registering a callback without `defer` makes the code more
-fragile by allowing the possibility that another contributor will make
-changes without considering the impact of those changes on the
-registered callbacks' execution contexts. Thus, `defer` should always
-be used. We offer the following rule to determine which form of
-`defer` should be used in a given situation:
+An instance of `Promise` that is deleted before it has transitioned out of `PENDING` is considered abandoned. The concept of abandonment was added late to the library so for backwards compatibility reasons we could not add a new state but instead needed to have it be a sub-state of `PENDING`.
 
-* If the callback being registered accesses the state of a process,
-  then it should be registered using `defer(pid, callback)`, where
-  `pid` is the PID of the process whose state is being accessed.
-* If the callback doesn't access any process state, or only makes use
-  of process variables that are captured _by value_ so that the
-  context of the process is not directly accessed when the callback is
-  executed, then it can be run in an arbitrary execution context
-  chosen by libprocess, and it should be registered using
-  `defer(callback)`.
+You can check if a future has been abandoned by doing `Future::isAbandoned()` and set up a callback using `Future::onAbandoned()`. Here's an example:
 
-
-### `ID`
-
-Generates a unique identifier string given a prefix. This is used to
-provide `PID` names.
-
-
-### `PID`
-
-A `PID` provides a level of indirection for naming a process without
-having an actual reference (pointer) to it (necessary for remote
-processes).
-
-<!---
 ```cpp
-using namespace process;
+using process::Future;
+using process::Promise;
 
 int main(int argc, char** argv)
 {
-  QueueProcess process;
-  spawn(process);
+  Promise<int>* promise = new Promise<int>();
+
+  Future<int> future = promise->future();
+
+  CHECK(!future.isAbandoned());
 
-  PID<QueueProcess> pid = process.self();
+  delete promise; // ABANDONMENT!
 
-  dispatch(pid, &QueueProcess:enqueue, 42);
+  CHECK_ABANDONED(future);
 
-  terminate(pid);
-  wait(pid);
+  CHECK_PENDING(future); // ALSO STILL PENDING!
 
   return 0;
 }
 ```
----->
 
----
-
-## <a name="futures-and-promises"></a> Futures and Promises
-
-The `Future` and `Promise` primitives are used to enable
-programmers to write asynchronous, non-blocking, and highly
-concurrent software.
-
-A `Future` acts as the read-side of a result which might be
-computed asynchronously. A `Promise`, on the other hand, acts
-as the write-side "container". We'll use some examples to
-explain the concepts.
+### <a name="futures-and-promises-composition"></a> Composition: `Future::then()`, `Future::repair()`, and `Future::recover()`
 
-First, you can construct a `Promise` of a particular type by
-doing the following:
+You can compose together asynchronous function calls using `Future::then()`, `Future::repair()`, and `Future::recover()`. To help understand the value of composition, we'll start with an example of how you might manually do this composition:
 
 ```cpp
-using namespace process;
+using process::Future;
+using process::Promise;
 
-int main(int argc, char** argv)
+// Returns an instance of `Person` for the specified `name`.
+Future<Person> find(const std::string& name);
+
+// Returns the mother (an instance of `Person`) of the specified `name`.
+Future<Person> mother(const std::string& name)
 {
-  Promise<int> promise;
+  // First find the person.
+  Future<Person> person = find(name);
+
+  // Now create a `Promise` that we can use to compose the two asynchronous calls.
+  Promise<Person>* promise = new Promise<Person>();
+
+  Future<Person> mother = promise->future();
+
+  // Here is the boiler plate that can be replaced by `Future::then()`!
+  person.onAny([](const Future<Person>& person) {
+    if (person.isFailed()) {
+      promise->fail(person.failure());
+    } else if (person.isDiscarded()) {
+      promise->discard();
+    }
+    CHECK_READY(person);
+    promise->set(find(person->mother));
+    delete promise;
+  });
 
-  return 0;
+  return mother;
 }
 ```
 
-A `Promise` is not copyable or assignable, in order to encourage
-strict ownership rules between processes (i.e., it's hard to
-reason about multiple actors concurrently trying to complete a
-`Promise`, even if it's safe to do so concurrently).
-
-You can get a `Future` from a `Promise` using the
-`Promise::future()` method:
+Using `Future::then()` this can be simplified to:
 
 ```cpp
-using namespace process;
-
-int main(int argc, char** argv)
-{
-  Promise<int> promise;
+using process::Future;
 
-  Future<int> future = promise.future();
+// Returns an instance of `Person` for the specified `name`.
+Future<Person> find(const std::string& name);
 
-  return 0;
+// Returns the mother (an instance of `Person`) of the specified `name`.
+Future<Person> mother(const std::string& name)
+{
+  return find(name)
+    .then([](const Person& person) {
+      return find(person.mother);
+    });
 }
 ```
 
-Note that the templated type of the future must be the exact
-same as the promise: you cannot create a covariant or
-contravariant future. Unlike `Promise`, a `Future` can be both
-copied and assigned:
+Each of `Future::then()`, `Future::repair()`, and `Future::recover()` takes a callback that will be invoked after certain transitions, captured by this table:
+
+| Transition | `Future::*()` |
+| ------ | -------------- |
+| `READY` | `Future::then(F&&)` |
+| `FAILED` | `Future::repair(F&&)` and `Future::recover(F&&)` |
+| `DISCARDED` | `Future::recover(F&&)` |
+| Abandoned (`PENDING` and `Future::isAbandoned()`) | `Future::recover(F&&)` |
+
+`Future::then()` allows you to _transform_ the type of the `Future` into a new type but both `Future::repair()` and `Future::recover()` must return the same type as `Future` because they may not get executed! Here's an example using `Future::recover()` to handle a failure:
 
 ```cpp
-using namespace process;
+using process::Future;
 
-int main(int argc, char** argv)
+// Returns an instance of `Person` for the specified `name`.
+Future<Person> find(const std::string& name);
+
+// Returns a parent (an instance of `Person`) of the specified `name`.
+Future<Person> parent(const std::string& name)
 {
-  Promise<int> promise;
+  return find(name)
+    .then([](const Person& person) {
+      // Try to find the mother and if that fails try the father!
+      return find(person.mother)
+        .recover([=](const Future<Person>&) {
+          return find(person.father);
+        });
+    });
+}
+```
 
-  Future<int> future = promise.future();
+> <br> **Be careful what you capture in your callbacks! Depending on the state of the future the callback may be executed from a different scope and what ever you captured may no longer be valid; see [Callback Semantics](#futures-and-promises-callback-semantics) for more details.** <br><br>
 
-  // You can copy a future.
-  Future<int> future2 = future;
+### <a name="futures-and-promises-discarding-and-composition"></a> Discarding and Composition
 
-  // You can also assign a future (NOTE: this future will never
-  // complete because the Promise goes out of scope, but the
-  // Future is still valid and can be used normally.)
-  future = Promise<int>().future();
+Doing a `Future::discard()` will _propagate_ through each of the futures composed with `Future::then()`, `Future::recover()`, etc. This is usually what you want, but there are two important caveats to look out for:
 
-  return 0;
-}
-```
+#### <a name="futures-and-promises-then-discards"></a> 1. `Future::then()` enforces discards
+
+The future returned by `Future::then()` _will not execute the callback if a discard has been requested._ That is, even if the future transitions to `READY`, `Future::then()` will still enforce the request to discard and transition the future to `DISCARDED`.
 
-The result encapsulated in the `Future`/`Promise` can be in one
-of four states: `PENDING`, `READY`, `FAILED`, `DISCARDED`. When
-a `Promise` is first created the result is `PENDING`. When you
-complete a `Promise` using the `Promise::set()` method the
-result becomes `READY`:
+> <br> These semantics are surprising to many, and, admittedly, the library may at one point in the future change the semantics and introduce a `discardable()` helper for letting people explicitly decide if/when they want a callback to be discarded. Historically, these semantics were chosen so that people could write infinite loops using `Future::then()` that could be interrupted with `Future::discard()`. The proper way to do infinte loops today is with `loop()`. <br><br>
+
+Here's an example to clarify this caveat:
 
 ```cpp
-using namespace process;
+using process::Future;
+using process::Promise;
 
 int main(int argc, char** argv)
 {
   Promise<int> promise;
 
-  Future<int> future = promise.future();
+  Future<std::string> future = promise.future()
+    .then([](int i) {
+      return stringify(i);
+    });
+
+  future.discard();
+
+  CHECK_PENDING(future);
 
   promise.set(42);
 
-  CHECK(future.isReady());
+  CHECK_DISCARDED(future); // EVEN THOUGH THE PROMISE COMPLETED SUCCESSFULLY!
 
   return 0;
 }
 ```
 
-> NOTE: `CHECK` is a macro from `gtest` which acts like an
-> `assert` but prints a stack trace and does better signal
-> management. In addition to `CHECK`, we've also created
-> wrapper macros `CHECK_PENDING`, `CHECK_READY`, `CHECK_FAILED`,
-> `CHECK_DISCARDED` which enables you to more concisely do
-> things like `CHECK_READY(future)` in your code. We'll use
-> those throughout the rest of this guide.
-
-TODO(benh):
-* Using `Future` and `Promise` between actors, i.e., `dispatch` returning a `Future`
-* `Promise::fail()`
-* `Promise::discard()` and `Future::discard()`
-* `Future::onReady()`, `Future::onFailed()`, `Future::onDiscarded()`
-* `Future::then()`, `Future::repair()`, `Future::after`
-* `defer`
-* `Future::await()`
-
-<!--
-#### `Future::then`
+#### <a name="futures-and-promises-undiscardable"></a> 2. Sometimes you want something to be `undiscardable()`
 
-`Future::then` allows to invoke callbacks once a future is completed.
+You may find yourself in a circumstance when you're referencing (or composing) futures but _you don't want a disard to propagate to the referenced future!_ Consider some code that needs to complete some expensive initialization before other functions can be called. We can model that by composing each function with the initialization, for example:
 
 ```cpp
-using namespace process;
+using process::Future;
 
-int main(int argc, char** argv)
+Future<T> foo()
 {
-  ...;
+  return initialization
+    .then([](...) {
+      return ...;
+    });
+}
+```
 
-  Future<int> i = dispatch(process, &QueueProcess<int>::dequeue);
+In the above example, if someone were to discard the future returned from `foo()` _they would also end up discarding the initialization!_
 
-  dispatch(process, &QueueProcess<int>::enqueue, 42);
+The real intention here is to compose with the initialization but not propagate discarding. This can be accomplished using `undiscardable()` which acts as a barrier to stop a discard from propagating through. Here's how the previous example would look with `undiscardable()`:
 
-  i.then([] (int i) {
-    // Use 'i'.
-  });
+```cpp
+using process::Future;
+using process::undiscardable;
 
-  ...;
+Future<T> foo()
+{
+  return undiscardable(initialization)
+    .then([](...) {
+      return ...;
+    });
 }
 ```
 
+### <a name="futures-and-promises-callback-semantics"></a> Callback Semantics
 
-### `Promise`
+There are two possible ways in which the callbacks to the `Future::onReady()` family of functions as well as the composition functions like `Future::then()` get invoked:
 
-A `promise` is an object that can fulfill a [futures](#future), i.e. assign a result value to it.
+1. By the caller of `Future::onReady()`, `Future::then()`, etc.
+2. By the caller of `Promise::set()`, `Promise::fail()`, etc.
 
+The first case occurs if the future is already transitioned to that state when adding the callback, i.e., if the state is already `READY` for either `Future::onReady()` or `Future::then()`, or `FAILED` for `Future::onFailed()` or `Future::recover()`, etc, then the callback will be executed immediately.
 
-```cpp
-using namespace process;
+The second case occurs if the future has not yet transitioned to that state. In that case the callback is stored and it is executed by the caller of `Promise::set()`, `Promise::fail()`, whoever is deleting the promise (for abandonment), etc.
 
-template <typename T>
-class QueueProcess : public Process<QueueProcess<T>>
-{
-public:
-  Future<T> dequeue()
-  {
-    return promise.future();
-  }
+_This means that it is critical to consider the synchronization that might be necessary for your code given that multiple possible callers could execute the callback!_
 
-  void enqueue(T t)
-  {
-    promise.set(t);
-  }
+We call these callback semantics _synchronous_, as opposed to _asynchronous_. You can use `defer()` in order to _asynchronously_ invoke your callbacks.
 
-private:
-  Promise<T> promise;
-};
-
-
-int main(int argc, char** argv)
-{
-  ...;
+> <br> Note that after the callbacks are invoked they are deleted, so any resources that you might be holding on to inside the callback will properly be released after the future transitions and it's callbacks are invoked. <br><br>
 
-  Future<int> i = dispatch(process, &QueueProcess<int>::dequeue);
+### <a name="futures-and-promises-check-overloads"></a> `CHECK()` Overloads
 
-  dispatch(process, &QueueProcess<int>::enqueue, 42);
+`CHECK()` is a macro from [Google Test](https://github.com/google/googletest) which acts like an `assert` but prints a stack trace and does better signal management. In addition to `CHECK()`, we've also created wrapper macros `CHECK_PENDING()`, `CHECK_READY()`, `CHECK_FAILED()`, `CHECK_DISCARDED()` which enables you to more concisely do things like `CHECK_READY(future)` in your tests.
 
-  i.await();
+<!--- TODO(benh):
 
-  ...;
-}
-```
+* `Future::after`
+* `Future::await()`
 
+--->
 
--->
 
 ## <a name="http"></a> HTTP
 
@@ -645,6 +468,234 @@ Future<Response> response = connection.send(request);
 It's also worth noting that if multiple requests are sent in succession on a
 `Connection`, they will be automatically pipelined.
 
+## <a name="processes"></a> Processes (aka Actors)
+
+An actor in libprocess is called a *process* (not to be confused by an operating system process).
+
+A process receives events that it processes one at a time. Because a process is only processing one event at a time there's no need for synchronization within the process.
+
+There are a few ways to create an event for a process, the most important including:
+
+* You can `send()` a process a message.
+* You can do a function `dispatch()` on a process.
+* You can send an `http::Request` to a process.
+
+That last one is exceptionally powerful; every process is also an HTTP-based service that you can communicate with using the HTTP protocol.
+
+### Process Lifecycle
+
+You create a process like any other class in C++ but extending from `Process`. `Process` uses the [curiously recurring template pattern (CRTP)](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) to simplify types for some of it's methods (you'll see this with `Process::self()` below).
+
+Practically you can think of a process as a combination of a thread and an object, except creating/spawning a process is very cheap (no actual thread gets created, and no stack gets allocated).
+
+Here's the simplest process you can create:
+
+```cpp
+using process::Process;
+
+class FooProcess : public Process<FooProcess> {};
+```
+
+You start a process using `spawn()`, stop a process using `terminate()`, and wait for it to terminate by using `wait()`:
+
+```cpp
+using process::Process;
+using process::spawn;
+using process::terminate;
+using process::wait;
+
+class FooProcess : public Process<FooProcess> {};
+
+int main(int argc, char** argv)
+{
+  FooProcess process;
+  spawn(process);
+  terminate(process);
+  wait(process);
+  return 0;
+}
+```
+
+#### Memory Management
+
+A process ***CAN NOT*** be deleted until after doing a `wait()`, otherwise you might release resources that the library is still using! To simplify memory management you can ask the library to delete the process for you after it has completely terminated. You do this by invoking `spawn()` and passing `true` as the second argument:
+
+```cpp
+using process::Process;
+using process::spawn;
+using process::terminate;
+using process::wait;
+
+class FooProcess : public Process<FooProcess> {};
+
+int main(int argc, char** argv)
+{
+  FooProcess* process = new FooProcess();
+  spawn(process, true); // <-- `process` will be automatically deleted!
+  terminate(process);
+  wait(process)
+  return 0;
+}
+```
+
+### Process Identity: `PID` and `UPID`
+
+A process is uniquely identifiable by it's process id which can be any arbitrary string (but only one process can be spawned at a time with the same id). The `PID` and `UPID` types encapsulate both the process id as well as the network address for the process, e.g., the IP and port where the process can be reached if libprocess was initialized with an IPv4 or IPv6 network address. You can get the `PID` of a process by calling it's `self()` method:
+
+```cpp
+using process::PID;
+using process::Process;
+using process::spawn;
+
+class FooProcess : public Process<FooProcess> {};
+
+int main(int argc, char** argv)
+{
+  FooProcess process;
+  spawn(process);
+
+  PID<FooProcess> pid = process.self();
+
+  return 0;
+}
+```
+
+A `UPID` is the "**u**ntyped" base class of `PID`.
+
+> If you turn on logging you might see a `PID`/`UPID` printed out as `id@ip:port`.
+
+### Process Event Queue (aka Mailbox)
+
+Each process has a queue of incoming `Event`'s that it processes one at a time. Other actor implementations often call this queue the "mailbox".
+
+There are 5 different kinds of events that can be enqueued for a process:
+
+* `MessageEvent`: a `Message` has been received.
+* `DispatchEvent`: a method on the process has been "dispatched".
+* `HttpEvent`: an `http::Request` has been received.
+* `ExitedEvent`: another process which has been [linked](#links) has terminated.
+* `TerminateEvent`: the process has been requested to terminate.
+
+An event is serviced one at a time by invoking the process' `serve()` method which by default invokes the process' `visit()` method corresponding to the underlying event type. Most actors don't need to override the implementation of `serve()` or `visit()` but can rely on higher-level abstractions that simplify serving the event (e.g., `route()`, which make it easy to set up handlers for an `HttpEvent`, discussed below in [HTTP](#http)).
+
+#### `MessageEvent`
+
+A `MessageEvent` gets enqueued for a process when it gets sent a `Message`, either locally or remotely. You use `send()` to send a message from within a process and `post()` to send a message from outside a process. A `post()` sends a message without a return address because there is no process to reply to. Here's a classic ping pong example using `send()`:
+
+```cpp
+using process::MessageEvent;
+using process::PID;
+using process::Process;
+using process::spawn;
+using process::terminate;
+using process::wait;
+
+class ClientProcess : public Process<ClientProcess>
+{
+public:
+  ClientProcess(const UPID& server) : server(server) {}
+
+  void initialize() override
+  {
+    send(server, "ping");
+  }
+
+  void visit(const MessageEvent& event) override
+  {
+    if (event.message.from == server &&
+        event.message.name == "pong") {
+      terminate(self());
+    }
+  }
+};
+
+class ServerProcess : public Process<ServerProcess>
+{
+public:
+protected:
+  void visit(const MessageEvent& event) override
+  {
+    if (event.message.name == "ping") {
+      send(event.message.from, "pong");
+    }
+    terminate(self());
+  }
+};
+
+int main(int argc, char** argv)
+{
+  PID<ServerProcess> server = spawn(new ServerProcess(), true);
+  PID<ClientProcess> client = spawn(new ClientProcess(server), true);
+
+  wait(server);
+  wait(client);
+
+  return 0;
+}
+```
+
+#### `DispatchEvent`
+
+TODO.
+
+#### `HttpEvent`
+
+TODO.
+
+#### `ExitedEvent`
+
+TODO.
+
+#### `TerminateEvent`
+
+TODO.
+
+### <a name="async-pimpl"></a> Processes and the Asynchronous Pimpl Pattern
+
+It's tedious to require everyone to have to explicitly `spawn()`, `terminate()`, and `wait()` for a process. Having everyone call `dispatch()` when they really just want to invoke a function (albeit asynchronously) is unfortnate too! To alleviate these burdenes, a common pattern that is used is to wrap a process within another class that performs the `spawn()`, `terminate()`, `wait()`, and `dispatch()`'s for you. Here's a typical example:
+
+```cpp
+class FooProcess : public Process<FooProcess>
+{
+public:
+  void foo(int i);
+};
+
+class Foo
+{
+public:
+  Foo()
+  {
+    process::spawn(process);
+  }
+
+  ~Foo()
+  {
+    process::terminate(process);
+    process::wait(process);
+  }
+
+  void foo(int i)
+  {
+    dispatch(process, &FooProcess::foo, i);
+  }
+
+private:
+  FooProcess process;
+};
+
+int main(int argc, char** argv)
+{
+  Foo foo;
+
+  foo.foo(42);
+
+  return 0;
+}
+```
+
+Anyone using `Foo` uses it similarly to how they would work with any other _synchronous_ object where they don't know, or need to know, the implementation details under the covers (i.e., that it's implemented using a process). This is similar to the [Pimpl](https://en.wikipedia.org/wiki/Opaque_pointer) pattern, except we need to `spawn()` and `terminate()/wait()` and rather than _synchronously_ invoking the underlying object we're _asynchronously_ invoking the underlying object using `dispatch()`.
+
 ## <a name="clock"></a> Clock Management and Timeouts
 
 Asynchronous programs often use timeouts, e.g., because a process that initiates
@@ -728,7 +779,6 @@ int main()
 }
 ```
 
-
 ## <a name="miscellaneous-primitives"></a> Miscellaneous Primitives
 
 ### `async`