template class EE::Task

Overview

The result of an operation that completes later. More…

#include <task.hpp>

template <typename T>
class Task {
public:
    // typedefs

    typedef T ValueType;

    // construction

    Task();
    Task(Task&&);
    Task(const Task&);

    // methods

    Task& operator=(Task&&);
    Task& operator=(const Task&);
    bool valid() const;
    TaskState state() const;
    bool isReady() const;
    bool isPending() const;
    TaskState wait() const;

    template <typename Rep, typename Period>
    TaskState waitFor(const std::chrono::duration<Rep, Period>& timeout) const;

    T takeValue();
    auto get();

    template <typename F>
    auto then(Executor& executor, F&& fn);

    template <typename F>
    auto then(F&& fn);

    template <typename F>
    void onComplete(F&& fn);

    static Task create(TaskResolver<T>& resolve);

    template <typename... Args>
    static Task ready(Args&&... args);
};

Detailed Documentation

The result of an operation that completes later.

The consumer’s end. The producer completes it through the TaskResolver that create() fills in. Task is move-only and carries no domain error semantics: fallible operations return Task <Result<T, E>>. Attaching a continuation consumes the task (std::move( task ).then( ... )), so only one continuation chain can ever exist.

  • then( executor, fn ): runs fn through the executor (for example the UI thread’s), even if the task has already completed. This is the normal way to use a result.

  • then( fn ): runs fn on whichever thread completes the task, or right away on the calling thread if it already has. For cheap transformations that touch no thread-bound state.

  • fn may return a value, void, or a Task; nested tasks are flattened into Task.

  • If the task is abandoned, fn is not called and the returned task is abandoned too.

  • onComplete( fn ) also observes abandonment: fn receives std::nullopt.

Construction

Task()

Creates an invalid task. Its state() is Abandoned.

Methods

TaskState wait() const

Blocks until the task is Ready or Abandoned.

This is a low-level bridge for worker-thread adapters and tests. It must never be called on the UI thread, nor on the thread that is expected to resolve the task.

template <typename Rep, typename Period>
TaskState waitFor(const std::chrono::duration<Rep, Period>& timeout) const

Blocks until the task completes or the timeout expires.

Returns:

The state on return.

T takeValue()

Extracts the value of a Ready task and consumes it. Precondition: isReady().

auto get()

Blocks until completion and returns the value, or std::nullopt if abandoned. Same threading restrictions as wait().

template <typename F>
auto then(Executor& executor, F&& fn)

Runs fn( value ) through executor once the task completes, even if it already has. The executor must outlive the task chain. See the class documentation.

template <typename F>
auto then(F&& fn)

Runs fn( value ) on the thread that completes the task, or right away on the calling thread if it already has. See the class documentation.

template <typename F>
void onComplete(F&& fn)

Observes the outcome without producing a new task, on the thread that completes the task (or right away on the calling thread if it already has). fn( std::optional<T> value ) receives the value, or std::nullopt if the task was abandoned; for Task<void>, fn( bool completed ) receives false if it was abandoned.

static Task create(TaskResolver<T>& resolve)
TaskResolver<int> resolve;
auto task = Task<int>::create( resolve );
pool.execute( [resolve] { resolve( 42 ); } );
return task;

Returns:

A pending task. resolve is set to the resolver that completes it (a resolver it held before is released).

template <typename... Args>
static Task ready(Args&&... args)

Returns:

A task that is already Ready with the given value.