Document Number: N2671=08-00181
Date: 2008-06-29
Project: Programming Language C++

Detlef Vollmann <dv@vollmann.ch>
Howard Hinnant <howard.hinnant@gmail.com>
Anthony Williams <anthony@justsoftwaresolutions.co.uk>

N2671: An Asynchronous Future Value: Proposed Wording

This paper provides full proposed wording for the mechanisms described in N2627. The main differences to N2627 are: Minor changes include the renaming of the timed wait functions and making some notes normative text.

Proposed changes to the Working Paper

Add to clause 30 "Thread support library" the following new subclause "Futures". This subclause describes components that a C++ program can use to retrieve in one thread the result (value or exception) from a function that has run in another thread. [Note: these components are not restricted to multi-threaded programs but can be useful in single-threaded programs as well. --end note]

Header <future>

namespace std {
    enum class future_errc
    {
        broken_promise,
        future_already_retrieved,
        promise_already_satisfied
    };

    template <> struct is_error_code_enum<future_errc> : public true_type {};

    constexpr error_code make_error_code(future_errc e);
    constexpr error_condition make_error_condition(future_errc e);

    extern const error_category* const future_category;

    class future_error;

    template <typename R> class unique_future;
    template <typename R> class unique_future<R&>;
    template <> class unique_future<void>;
    template <typename R> class shared_future;
    template <typename R> class shared_future<R&>;
    template <> class shared_future<void>;
    template <typename R> class promise;

}

Error handling

extern const error_category* const future_category;

future_category shall point to a statically initialized object of a type derived from class error_category.

The object's default_error_condition and equivalent virtual functions shall behave as specified for the class error_category. The object name virtual function shall return a pointer to the string "FUTURE".

constexpr error_code make_error_code(future_errc e);

Returns: error_code(static_cast<int>(e), future_category).

constexpr error_condition make_error_condition(future_errc e);

Returns: error_condition(static_cast<int>(e), future_category).

Class future_error

namespace std
{

// put into sub-namespace and call it ???
    class future_error : public logic_error
    {
    public:
        future_error(error_code ec); // exposition only, not intended for general use

        const error_code& code() const throw();
        const char*       what() const throw();
    };
}
    const error_code& code() const throw();

Returns: ec from the constructor.

    const char *what() const throw();

Returns: An NTBS incorporating code().message().

Class template unique_future

[[Note to the editor: this template has two specializations for void and R&, where only the return type for the get() functions differ. How is this best specified? --end note to editor]]
namespace std
{
template <typename R>
class unique_future
{
public:
    unique_future(unique_future &&);
    unique_future(unique_future const & rhs) = delete;

    ~unique_future();

    unique_future& operator=(unique_future const & rhs) = delete;

    // retrieving the value
    R&& get();
    R& unique_future<R&>::get(); // this is for the specialization unique_future<R&>
    void unique_future<void>::get(); // this is for the specialization unique_future<void>

    // functions to check state, and wait for ready
    bool is_ready() const;
    bool has_exception() const;
    bool has_value() const;

    void wait() const;
    template <class Rep, class Period>>
    bool wait_for(const chrono::duration<Rep, Period>& rel_time) const;
    template <class Clock, class Duration>
    bool wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
};
}
    unique_future(const unique_future&& rhs);

Effects: MoveConstructs a unique_future whose associated state is the same as the state of rhs before. The associated state is the state and the (possibly not yet evaluated) result (value or exception) associated with the promise that provided the original future.

Postconditions: rhs can be safely destroyed.

    ~unique_future();

Effects: destroys *this and its associated state if no other object refers to that.

    R&& get();
    R& unique_future<R&>::get(); // this is for the specialization unique_future<R&>
    void unique_future<void>::get(); // this is for the specialization unique_future<void>

Effects: Retrieves the value stored in the associated state.

Sychronization: If *this is associated with a promise, the completion of set_value() or set_exception() to that promise happens before ([intro.multithread]) get() returns.

Returns: If the result type R is a reference, returns the stored reference. If R is void, there is no return value. Otherwise, returns an rvalue-reference to the value stored in the asynchronous result.

Throws: the stored exception, if an exception was stored and not retrieved before.

The return value of is_ready is unspecified after a call to get().

It is unspecified what happens when get() is called a second time on the same unique_future.

    bool is_ready() const;

Returns: true if the associated state holds a value or an exception ready for retrieval.

    bool has_exception() const;

Returns: true if is_ready() == true and the associated state contains an exception, false otherwise.

    bool has_value() const;

Returns: true if is_ready() == true and the associated state contains a value, false otherwise.

    void wait() const;

Effects: Blocks until *this is ready.

Sychronization: If *this is associated with a promise, the completion of set_value() or set_exception() to that promise happens before ([intro.multithread]) wait() returns.

Postconditions: is_ready() == true.

    template <class Rep, class Period>>
    bool wait_for(const chrono::duration<Rep, Period>& rel_time) const;

Effects: Blocks until *this is ready or until rel_time elapsed.

Returns: true if the function returns because *this is ready, false otherwise.

Postconditions: is_ready() equals the return value.

    template <class Clock, class Duration>
    bool wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;

Same as wait_for(), except that it blocks until abs_time is reached if the associated state is not ready.

Class template shared_future

[[Note to the editor: this template has two specializations for void and R&, where only the return type for the get() functions differ. How is this best specified? --end note to editor]]
namespace std
{
template <typename R>
class shared_future
{
public:
    shared_future(shared_future const & rhs);

    shared_future(unique_future<R>);

    ~shared_future();

    shared_future & operator=(shared_future const & rhs) = delete;

    // retrieving the value
    R const & get() const;
    R& shared_future<R&>::get() const; // this is for the specialization shared_future<R&>
    void shared_future<void>::get() const; // this is for the specialization shared_future<void>

    // functions to check state, and wait for ready
    bool is_ready() const;
    bool has_exception() const;
    bool has_value() const;

    void wait() const;
    template <class Rep, class Period>>
    bool wait_for(const chrono::duration<Rep, Period>& rel_time) const;
    template <class Clock, class Duration>
    bool wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
};
}
    shared_future(const shared_future& rhs);

Effects: CopyConstructs a shared_future whose associated state is the same as the state of rhs before. The associated state is the state and the (possibly not yet evaluated) result (value or exception) associated with the promise that provided the original future.

    shared_future(const unique_future<R> rhs);

Effects: MoveConstructs a shared_future whose associated state is the same as the state of rhs before.

Postconditions: rhs can be safely destroyed.

    ~shared_future();

Effects: destroys *this and its associated state if no other object refers to that.

    R const & get() const;
    R& shared_future<R&>::get() const; // this is for the specialization shared_future<R&>
    void shared_future<void>::get() const; // this is for the specialization shared_future<void>

Effects: retrieves the value stored in the associated state.

Sychronization: If *this is associated with a promise, the completion of set_value() or set_exception() to that promise happens before ([intro.multithread]) get() returns.

Returns: If the result type R is a reference, returns the stored reference. If R is void, there is no return value. Otherwise, returns a const reference to the value stored in the asynchronous result.

Throws: the stored exception, if an exception was stored and not retrieved before.

    bool is_ready() const;

Returns: true if the associated state holds a value or an exception ready for retrieval.

    bool has_exception() const;

Returns: true if is_ready() == true and the associated state contains an exception, false otherwise.

    bool has_value() const;

Returns: true if is_ready() == true and the associated state contains a value, false otherwise.

    void wait() const;

Effects: Blocks until *this is ready.

Sychronization: If *this is associated with a promise, the completion of set_value() or set_exception() to that promise happens before ([intro.multithread]) wait() returns.

Postconditions: is_ready() == true.

    template <class Rep, class Period>>
    bool wait_for(const chrono::duration<Rep, Period>& rel_time) const;

Effects: Blocks until *this is ready or until rel_time elapsed.

Returns: true if the functions returns because *this is ready, false otherwise.

Postconditions: is_ready() equals the return value.

    template <class Clock, class Duration>
    bool wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;

Same as wait_for(), except that it blocks until abs_time is reached if the associated state is not ready.

Class template promise

[[Note to the editor: this template has two specializations for void and R&, where only the signatures for the set_value() functions differ. How is this best specified? --end note to editor]]
namespace std
{
template <typename R>
class promise
{
public:
    promise();
    template <class Allocator>
        promise(allocator_arg_t, Allocator const A&);
    promise(promise && rhs);
    template <class Allocator>
        promise(allocator_arg_t, Allocator const A&,
                promise & rhs);
    promise(promise const & rhs) = delete;
    ~promise();

    // Assignment
    promise & operator=(promise && rhs);
    promise & operator=(promise const & rhs) = delete;
    void swap(promise& other);

    // Result retrieval
    unique_future<R> get_future();

    void set_value(R const & r);
    void set_value(R && r);
    void promise<R&>::set_value(R & r); // this is for the specialization promise<R&>
    void promise<void>::set_value(); // this is for the specialization promise<void>
    void set_exception(exception_ptr p);
};

template <typename R, class Alloc>
    struct uses_allocator&promise<R>, Alloc>;

template <typename R>
    struct constructible_with_allocator_prefix<promise<T2>>;
}
template <typename R, class Alloc>
    struct uses_allocator&promise<R>, Alloc>
     : true_type { };

Requires: Alloc shall be an Allocator ([allocator.requirements] 20.1.2)

Remarks: Specialization of this trait informs other library components that promise can be constructed with an allocator, even though it does not have an allocator_type associated type.

template <typename R>
    struct constructible_with_allocator_prefix<promise<T2>>
     : true_type { };

Remarks: Specialization of this trait informs other library components that a promise can always be constructed with an allocator prefix argument.

    promise();
    template <class Allocator>
        promise(allocator_arg_t, Allocator const &a);

Effects: constructs a promise and an associated state, using a if given for allocating the memory for the associated state.

    promise(promise && rhs);
    template <class Allocator>
        promise(allocator_arg_t, Allocator const A&,
                promise & rhs);

Effects: MoveConstructs a promise whose associated state is the same as the state of rhs before.

Postcondition: rhs has no associated state.

    ~promise();

Effects: destroys *this and its associated state if no other object refers to it. If another object refers to the associated state and that state is not ready, sets that state to ready and stores a future_error exception with error code broken_promise as result.

    promise & operator=(promise && rhs);

Effects: MoveAssigns its associated state to rhs.

Postcondition: *this has no associated state.

Returns: *this.

Throws: nothing.

    void swap(promise& other);

Effects: swap(*this, other);

Throws: nothing.

    unique_future<R> get_future();

Returns: a unique_future<R> with the same associated state as *this.

Throws: future_error if *this has no associated state.

Error condition: future_already_retrieved if *this has no associated state.

    void set_value(R const & r);
    void set_value(R && r);
    void promise<R&>::set_value(R & r); // this is for the specialization promise<R&>
    void promise<void>::set_value(); // this is for the specialization promise<void>

Effects: stores r in the associated state and sets that state to ready. Any blocking waits on the associated state are woken up.

Throws: future_error if its associated state is already ready.

Error condition: promise_already_satisfied if its associated state is already ready.

    void set_exception(exception_ptr p);

Effects: stores p in the associated state sets that state to ready. Any blocking waits on the associated state are woken up.

Throws: future_error if its associated state is already ready.

Error condition: promise_already_satisfied if its associated state is already ready.