This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Resolved status.

### 1507. `promise::XXX_at_thread_exit` functions have no synchronization requirements

Section: 33.10.6 [futures.promise] Status: Resolved Submitter: INCITS Opened: 2010-08-25 Last modified: 2016-01-28

Priority: Not Prioritized

View other active issues in [futures.promise].

View all other issues in [futures.promise].

View all issues with Resolved status.

Discussion:

Addresses US-199

`promise::XXX_at_thread_exit` functions have no synchronization requirements. Specifying synchronization for these member functions requires coordinating with the words in 33.10.6 [futures.promise]/21 and 25, which give synchronization requirements for `promise::set_value` and `promise::set_exception` (33.10.6 [futures.promise] p. 26 ff., p. 29 ff.).

[ Resolution proposed by ballot comment: ]

> Change 33.10.6 [futures.promise]/21 to mention `set_value_at_thread_exit` and `set_exception_at_thread_exit;` with this text, replace 33.10.6 [futures.promise]/25 and add two new paragraphs, after 33.10.6 [futures.promise]/28 and 33.10.6 [futures.promise]/31.

[2011-03-8: Lawrence comments and drafts wording]

This comment applies as well to other `*_at_thread_exit` functions. The following resolution adds synchronization paragraphs to all of them and edits a couple of related synchronization paragraphs.

[2011-03-09: Hans and Anthony add some improvements]

[2011-03-19: Detlef comments]

In regard to the suggested part:

> These operations do not provide any ordering guarantees with respect to other operations, except through operations on futures that reference the same shared state.

I would like this to change to:

> These operations do not provide any ordering guarantees with respect to other operations on the same promise object. [Note: They synchronize with calls to operations on objects that refer to the same shared state according to 33.10.5 [futures.state]. — end note]

The current proposed resolution has exactly the same paragraph at for places. I propose to have it only once as new paragraph 2.

This also covers 1504 (US-196) and 1505 (US-197). US-197 is essentially rejected with this resolution, but a clarification is added that the normative wording is already in 33.10.5 [futures.state].

> Proposed Resolution

> 1. Edit 33.6.4.2 [thread.mutex.requirements.mutex] paragraph 5 as follows:

>    > 5 The implementation shall provide lock and unlock operations, as described below. The implementation shall serialize those operations.For purposes of determining the existence of a data race, these behave as atomic operations (6.9.2 [intro.multithread]). The lock and unlock operations on a single mutex shall appear to occur in a single total order. [Note: this can be viewed as the modification order (6.9.2 [intro.multithread]) of the mutex. — end note] [ Note: Construction and destruction of an object of a mutex type need not be thread-safe; other synchronization should be used to ensure that mutex objects are initialized and visible to other threads. — end note ]

> 2. Edit 33.7 [thread.condition] paragraphs 6-9 as follows:

>    >     void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);

>    > > -6- Requires: `lk` is locked by the calling thread and either

>    > > - no other thread is waiting on `cond`, or
>    > > - `lk.mutex()` returns the same value for each of the lock arguments supplied by all concurrently waiting (via `wait`, `wait_for`, or `wait_until`) threads.

>    > > -7- Effects: transfers ownership of the lock associated with `lk` into internal storage and schedules `cond` to be notified when the current thread exits, after all objects of thread storage duration associated with the current thread have been destroyed. This notification shall be as if

>    > > >     lk.unlock();
>    > > >     cond.notify_all();

>    > > -?- Synchronization: The call to `notify_all_at_thread_exit` and the completion of the destructors for all the current thread's variables of thread storage duration synchronize with (6.9.2 [intro.multithread]) calls to functions waiting on `cond`.

>    > > -8- Note: The supplied lock will be held until the thread exits, and care must be taken to ensure that this does not cause deadlock due to lock ordering issues. After calling `notify_all_at_thread_exit` it is recommended that the thread should be exited as soon as possible, and that no blocking or time-consuming tasks are run on that thread.

>    > > -9- Note: It is the user's responsibility to ensure that waiting threads do not erroneously assume that the thread has finished if they experience spurious wakeups. This typically requires that the condition being waited for is satisfied while holding the lock on `lk`, and that this lock is not released and reacquired prior to calling `notify_all_at_thread_exit`.

> 3. Edit 33.10.6 [futures.promise], paragraphs 14-27 as follows:

>    >     void promise::set_value(const R& r);
>    >     void promise::set_value(R&& r);
>    >     void promise<R&>::set_value(R& r);
>    >     void promise<void>::set_value();

>    > > -14- Effects: atomically stores the value `r` in the shared state and makes that state ready (33.10.5 [futures.state]).

>    > > -15- Throws:
>    > > - `future_error` if its shared state already has a stored value or exception, or
>    > > - for the first version, any exception thrown by the copy constructor of `R`, or
>    > > - for the second version, any exception thrown by the move constructor of `R`.

>    > > -16- Error conditions:

>    > > - `promise_already_satisfied` if its shared state already has a stored value or exception.
>    > > - `no_state` if `*this` has no shared state.

>    > > -17- Synchronization: calls to `set_value` and `set_exception` on a single `promise` object are serialized. [ Note: And they synchronize and serialize with other functions through the referred shared state. — end note ]For purposes of determining the existence of a data race, `set_value`, `set_exception`, `set_value_at_thread_exit`, and `set_exception_at_thread_exit` behave as atomic operations (6.9.2 [intro.multithread]) on the memory location associated with the `promise`. Calls to these operations on a single promise shall appear to occur in a single total order. [Note: this can be viewed as the modification order (6.9.2 [intro.multithread]) of the promise. — end note] These operations do not provide any ordering guarantees with respect to other operations, except through operations on futures that reference the same shared state.

>    >     void set_exception(exception_ptr p);

>    > > -18- Effects: atomically stores the exception pointer `p` in the shared state and makes that state ready (33.10.5 [futures.state]).

>    > > -19- Throws: `future_error` if its shared state already has a stored value or exception.

>    > > -20- Error conditions:

>    > > - `promise_already_satisfied` if its shared state already has a stored value or exception.
>    > > - `no_state` if `*this` has no shared state.

>    > > -21- Synchronization: calls to `set_value` and `set_exception` on a single `promise` object are serialized. [ Note: And they synchronize and serialize with other functions through the referred shared state. — end note ]For purposes of determining the existence of a data race, `set_value`, `set_exception`, `set_value_at_thread_exit`, and `set_exception_at_thread_exit` behave as atomic operations (6.9.2 [intro.multithread]) on the memory location associated with the `promise`. Calls to these operations on a single promise shall appear to occur in a single total order. [Note: this can be viewed as the modification order (6.9.2 [intro.multithread]) of the promise. — end note] These operations do not provide any ordering guarantees with respect to other operations, except through operations on futures that reference the same shared state.

>    >     void promise::set_value_at_thread_exit(const R& r);
>    >     void promise::set_value_at_thread_exit(R&& r);
>    >     void promise<R&>::set_value_at_thread_exit(R& r);
>    >     void promise<void>::set_value_at_thread_exit();

>    > > -22- Effects: Stores the value `r` in the shared state without making that state ready immediately. Schedules that state to be made ready when the current thread exits, after all objects of thread storage duration associated with the current thread have been destroyed.

>    > > -23- Throws: `future_error` if an error condition occurs.

>    > > -24- Error conditions:
>    > > - `promise_already_satisfied` if its shared state already has a stored value or exception.
>    > > - `no_state` if `*this` has no shared state.

>    > > -??- Synchronization: For purposes of determining the existence of a data race, `set_value`, `set_exception`, `set_value_at_thread_exit`, and `set_exception_at_thread_exit` behave as atomic operations (6.9.2 [intro.multithread]) on the memory location associated with the `promise`. Calls to these operations on a single promise shall appear to occur in a single total order. [Note: this can be viewed as the modification order (6.9.2 [intro.multithread]) of the promise. — end note] These operations do not provide any ordering guarantees with respect to other operations, except through operations on futures that reference the same shared state.

>    >     void promise::set_exception_at_thread_exit(exception_ptr p);

>    > > -25- Effects: Stores the exception pointer `p` in the shared state without making that state ready immediately. Schedules that state to be made ready when the current thread exits, after all objects of thread storage duration associated with the current thread have been destroyed.

>    > > -26- Throws: `future_error` if an error condition occurs.

>    > > -27- Error conditions:
>    > > - `promise_already_satisfied` if its shared state already has a stored value or exception.
>    > > - `no_state` if `*this` has no shared state.

>    > > -??- Synchronization: For purposes of determining the existence of a data race, `set_value`, `set_exception`, `set_value_at_thread_exit`, and `set_exception_at_thread_exit` behave as atomic operations (6.9.2 [intro.multithread]) on the memory location associated with the `promise`. Calls to these operations on a single promise shall appear to occur in a single total order. [Note: this can be viewed as the modification order (6.9.2 [intro.multithread]) of the promise. — end note] These operations do not provide any ordering guarantees with respect to other operations, except through operations on futures that reference the same shared state.

> 4. Edit 33.10.10.2 [futures.task.members], paragraph 15-21 as follows:

>    >     void operator()(ArgTypes... args);

>    > > -15- Effects: `INVOKE(f, t1, t2, ..., tN, R)`, where `f` is the stored task of `*this` and `t1`, `t2`, `...`, `tN` are the values in `args...`. If the task returns normally, the return value is stored as the asynchronous result in the shared state of `*this`, otherwise the exception thrown by the task is stored. The shared state of `*this` is made ready, and any threads blocked in a function waiting for the shared state of `*this` to become ready are unblocked.

>    > > -16- Throws: a `future_error` exception object if there is no shared state or the stored task has already been invoked.

>    > > -17- Error conditions:
>    > > - `promise_already_satisfied` if the shared state is already ready.
>    > > - `no_state` if `*this` has no shared state.

>    > > -18- Synchronization: a successful call to `operator()` synchronizes with (6.9.2 [intro.multithread]) a call to any member function of a `future` or `shared_future` object that shares the shared state of `*this`. The completion of the invocation of the stored task and the storage of the result (whether normal or exceptional) into the shared state synchronizes with (6.9.2 [intro.multithread]) the successful return from any member function that detects that the state is set to ready. [ Note: `operator()` synchronizes and serializes with other functions through the shared state. — end note ]

>    >     void make_ready_at_thread_exit(ArgTypes... args);

>    > > -19- Effects: `INVOKE(f, t1, t2, ..., tN, R)`, where `f` is the stored task and `t1`, `t2`, `...`, `tN` are the values in `args...`. If the task returns normally, the return value is stored as the asynchronous result in the shared state of `*this`, otherwise the exception thrown by the task is stored. In either case, this shall be done without making that state ready (33.10.5 [futures.state]) immediately. Schedules the shared state to be made ready when the current thread exits, after all objects of thread storage duration associated with the current thread have been destroyed.

>    > > -20- Throws: `future_error` if an error condition occurs.

>    > > -21- Error conditions:
>    > > - `promise_already_satisfied` if the shared state already has a stored value or exception.
>    > > - `no_state` if `*this` has no shared state.

>    > > -??- Synchronization: a successful call to `make_ready_at_thread_exit` synchronizes with (6.9.2 [intro.multithread]) a call to any member function of a `future` or `shared_future` object that shares the shared state of `*this`. The completion of

>    > > - the invocation of the stored task and the storage of the result (whether normal or exceptional) into the shared state

>    > > - the destructors for all the current thread's variables of thread storage duration

>    > > synchronize with (6.9.2 [intro.multithread]) the successful return from any member function that detects that the state is set to ready. [Note: `make_ready_at_thread_exit` synchronizes and serializes with other functions through the shared state. — end note]

Proposed resolution:

Resolved 2011-03 Madrid meeting by paper N3278