Executors

First off, in the futures crate, executors are objects that can spawn a Future<Item = (), Error = !> as a new task. There are two key executors to be familiar with.

The ThreadPool executor

The simplest executor is futures::executor::ThreadPool, which schedules tasks onto a fixed pool of OS threads. Splitting the tasks across multiple OS threads means that even if a particular tick invocation takes a long time, other tasks can continue to make progress on other threads.

Setup and usage is very straightforward:

  1. // Set up the thread pool, which spins up worker threads behind the scenes.
  2. let exec = ThreadPool::new();
  3. // Spawn tasks onto the thread pool.
  4. exec.spawn(my_task);
  5. exec.spawn(other_task);

We’ll see later on a variety of ways to communicate with spawned tasks.

Note that, because the task will be executed on arbitrary threads, it is required to be Send and 'static.

The CurrentThread executor

The futures crate also provides a single-threaded executor called CurrentThread, similar in spirit to the one that we built. The key difference from the ThreadPool executor is that CurrentThread can execute non-Send and non-'static tasks. This is possible because the executor is run via an explicit call from the current thread:

  1. // start up the CurrentThread executor, which by default runs until all spawned
  2. // tasks are complete:
  3. CurrentThread::run(|_| {
  4. CurrentThread::spawn(my_task);
  5. CurrentThread::spawn(other_task);
  6. })

The tradeoffs between ThreadPool and CurrentThread are explained in more detail later in the book.

Spurious wakeups

In general, executors guarantee that they will call get any time a task is awoken. However, they may also call get at arbitrary other times. Thus, tasks cannot assume that a call to get means progress is possible; they should always re-attempt the operation that previously blocked them, and be prepared to wait again.

Exercises

  • Rewrite the example from the previous section to use the ThreadPool executor.
  • Rewrite the example from the previous section to use the CurrentThread executor.
  • What are the tradeoffs between using these two executors for the timer example?