Skip to content
Amanieu d'Antras edited this page Nov 27, 2013 · 16 revisions

task

class task<Result> {
    // Result type of the task
    typedef Result result_type;

    // Create an empty task object (operator bool returns false)
    task();

    // Movable but not copyable
    task(const task&) = delete;
    task(task&&);
    task& operator=(const task&) = delete;
    task& operator=(task&&);

    // Destroys the task object. Does not wait for the task to finish.
    ~task();

    // Returns true if this task object contains a reference to a valid task.
    // If this is not the case then all other functions throw std::invalid_argument.
    explicit operator bool() const;

    // Waits for the task to complete and then retrieves the result of the task.
    // If the task contains an exception then that exception is rethrown. The
    // result is moved out of the task, and then the task is cleared (operator
    // bool returns false).
    Result get();

    // Waits for the task to have finished executing
    void wait() const;

    // Returns whether the task has finished executing
    bool ready() const;

    //
    task<T> then(Func f); // T = result type of f
    task<T> then(scheduler& sched, Func f); // T = result type of f

    //
    shared_task<Result> share();
};

class shared_task<Result> {
    typedef Result result_type;
    shared_task();
    shared_task(const shared_task&);
    shared_task(shared_task&&);
    shared_task& operator=(const shared_task&);
    shared_task& operator=(shared_task&&);
    ~shared_task();
    explicit operator bool() const;
    void get() const; // If Result is void
    Result& get() const; // If Result is a reference type
    const Result& get() const; // Otherwise
    void wait() const;
    bool ready() const;
    task<T> then(Func f) const; // T = result type of f
    task<T> then(scheduler& sched, Func f) const; // T = result type of f
};

// Create a task already containing the given value
task<T> make_task(T value);
task<void> make_task();

// Spawn a task to run the given function, optionally using the given scheduler
// instead of the default. The default scheduler can be overriden by defining
// the LIBASYNC_DEFAULT_SCHEDULER preprocessor macro.
task<T> spawn(Func f); // T = result type of f
task<T> spawn(scheduler& sched, Func f); // T = result type of f

local_task

class local_task<Func> {
    // Local tasks can only be created using local_spawn()
    local_task() = delete;

    // Local tasks are non-movable and non-copyable
    local_task(const local_task&) = delete;
    local_task(local_task&&) = delete;
    local_task& operator=(const local_task&) = delete;
    local_task& operator=(local_task&&) = delete;

    // The destructor implicitly waits for the task to finish
    ~local_task();

    // Waits for the task to complete and then retrieves the result of the task.
    // If the task contains an exception then that exception is rethrown. The
    // result is moved out of the task.
    void get(); // If Result == void
    Result& get(); // If Result is a reference type
    Result get(); // Otherwise

    // Waits for the task to have finished executing
    void wait() const;

    // Returns whether the task has finished executing
    bool ready() const;
};

// Spawn a local task to run the given function, optionally using the given
// scheduler instead of the default. The default scheduler can be overriden by
// defining the LIBASYNC_DEFAULT_SCHEDULER preprocessor macro.
local_task<Func> local_spawn(Func f);
local_task<Func> local_spawn(scheduler& sched, Func f);

event_task

class event_task<Result> {
    event_task();
    event_task(const event_task&) = delete;
    event_task(event_task&&);
    event_task& operator=(const event_task&) = delete;
    event_task& operator=(event_task&&);
    ~event_task();
    task<Result> get_task() const;
    bool set() const; // If Result == void
    bool set(Result& r) const; // If Result is a reference type
    bool set(Result&& r) const; // Otherwise
    bool set(const Result& r) const; // Otherwise
    bool set_exception(std::exception_ptr except) const;
    bool cancel() const;
};

when_all/when_any

// For T = void, return value is task<size_t>
task<std::pair<size_t, T>> when_any(task<T>/shared_task<T>... tasks); // All T must be the same
task<std::pair<size_t, T>> when_any(Iter begin, Iter end); // Range of task<T> or shared_task<T>
task<std::pair<size_t, T>> when_any(Range tasks); // Range of task<T> or shared_task<T>

struct void_ {};
task<std::tuple<T...>> when_all(task<T>/shared_task<T>... tasks); // Allows different T types, void becomes void_

// For T = void, return value is task<void>
task<std::vector<T>> when_all(Iter begin, Iter end); // Range of task<T> or shared_task<T>
task<std::vector<T>> when_all(Range tasks); // Range of task<T> or shared_task<T>

Cancellation

// Exception thrown by cancel_current_task
struct task_canceled {};

// A cancellation token is a boolean flag which indicates a cancellation request
class cancellation_token {
    // A token is initialized to the 'not canceled' state
    cancellation_token();

    // Tokens are non-movable and non-copyable
    cancellation_token(const cancellation_token&) = delete;
    cancellation_token(cancellation_token&&) = delete;
    cancellation_token& operator=(const cancellation_token&) = delete;
    cancellation_token& operator=(cancellation_token&&) = delete;

    // Returns whether the token has been canceled
    bool is_canceled() const;

    // Sets the token to the canceled state
    void cancel();
};

// Throws a task_canceled exception
void cancel_current_task();

// Calls cancel_current_task() if the token is canceled
void interruption_point(const cancellation_token& token);

Task scheduling

class task_run_handle {
    // Task handles are movable but not copyable
    task_run_handle(const task_run_handle&) = delete;
    task_run_handle(task_run_handle&&);
    task_run_handle& operator=(const task_run_handle&) = delete;
    task_run_handle& operator=(task_run_handle&&);

    // Executes the task and invalidates the handle
    void run();

    // Same as before, but uses the given wait handler
    void run_with_wait_handler(wait_handler handler)

    // Convert to and from void pointer
    void* to_void_ptr();
    static task_handle from_void_ptr(void*);
};

class task_wait_handle {
    // Non-copyable and non-movable
    task_wait_handle(const task_wait_handle&) = delete;
    task_wait_handle(task_wait_handle&&) = delete;
    task_wait_handle& operator=(const task_wait_handle&) = delete;
    task_wait_handle& operator=(task_wait_handle&&) = delete;

    // Check if the task has finished
    bool ready() const;

    // Queue a function to be executed when the task has finished executing.
    template<typename Func> void on_finish(Func&& func)
};

// Set the wait handler for the current thread. This is used to allow a thread to do useful work while waiting for a task to complete.
typedef void (*wait_handler)(task_wait_handle t);
wait_handler set_thread_wait_handler(wait_handler w);

// Scheduler interface, all schedulers derive from this
class scheduler {
    virtual void schedule(task_run_handle t) = 0;
};

// Built-in scheduler implementations
scheduler& threadpool_scheduler();
scheduler& inline_scheduler();
scheduler& thread_scheduler();

Parallel algorithms

// Range object representing 2 iterators
class range<Iter> {
    Iter begin() const;
    Iter end() const;
};

// Create a range
range<Iter> make_range(Iter begin, Iter end);

// Integer range between 2 integers
class int_range<T> {
    class iterator;
    iterator begin() const;
    iterator end() const;
};

// Create an integer range
int_range<T> irange(T begin, T end);

// Partitioners which wrap a range and split it between threads. A partitioner is just a range with an additional split() function.
<detail> static_partitioner(Range&& range);
<detail> static_partitioner(Range&& range, size_t grain);
<detail> auto_partitioner(Range&& range);

// Convert a range to a partitioner. This is a utility function for implementing new parallel algorithms. If the argument is already a partitioner then a copy of it is returned.
decltype(auto_partitioner(range)) to_partitioner(Range&& range);
Partitioner to_partitioner(Partitioner&& partitioner);

// Run a set of functions in parallel, optionally using a scheduler
void parallel_invoke(Func&&... funcs);
void parallel_invoke(scheduler& sched, Func&&... funcs);

// Run a function over a range in parallel. The range parameter can also be a partitioner to explicitly control how jobs are distributed to threads.
void parallel_for(Range&& range, const Func& func);
void parallel_for(scheduler& sched, Range&& range, const Func& func);

// Reduce a range in parallel. The range parameter can also be a partitioner to explicitly control how jobs are distributed to threads.
Result parallel_reduce(Range&& range, const Result& initial, const ReduceFunc& reduce);
Result parallel_reduce(scheduler& sched, Range&& range, const Result& initial, const ReduceFunc& reduce);

// Apply a function to a range and reduce it. The range parameter can also be a partitioner to explicitly control how jobs are distributed to threads.
Result parallel_map_reduce(Range&& range, const Result& initial, const MapFunc& map, const ReduceFunc& reduce);
Result parallel_map_reduce(scheduler& sched, Range&& range, const Result& initial, const MapFunc& map, const ReduceFunc& reduce);
Clone this wiki locally