This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
packaged_task::operator=
should abandon its shared stateSection: 32.10.10.2 [futures.task.members] Status: New Submitter: Jonathan Wakely Opened: 2024-09-19 Last modified: 2024-10-03
Priority: 3
View all other issues in [futures.task.members].
View all issues with New status.
Discussion:
The packaged_task
move assignment operator is specified to release
the previous shared state. This means it releases ownership, but does
not make the shared state ready. Any future that shares ownership of the
shared state will never receive a result, because the provider is gone.
This means that any thread that waits on the future will block forever.
There is a note on packaged_task::reset()
which claims that assignment
abandons the state, which is not supported by any normative wording:
void reset();-26- Effects: As if
*this = packaged_task(std::move(f))
, wheref
is the task stored in*this
.[Note 2: This constructs a new shared state for
*this
. The old state is abandoned (32.10.5 [futures.state]). — end note]
Presumably, the intended behaviour of assignment was to abandon the
shared state,
i.e. make it ready with a broken_promise
error, and then release it.
That is what the std::promise
move assignment does
(see 32.10.6 [futures.promise] p9).
Both libstdc++ and libc++ abandon the state, despite what the standard says.
[2024-10-02; Reflector poll]
Set priority to 3 after reflector poll.
Previous resolution [SUPERSEDED]:
This wording is relative to N4988.
Modify 32.10.10.2 [futures.task.members] as indicated:
packaged_task& operator=(packaged_task&& rhs) noexcept;-11- Effects:
- (11.1) —
ReleasesAbandons any shared state (32.10.5 [futures.state]);- (11.2) — calls
packaged_task(std::move(rhs)).swap(*this)
.-?- Returns:
*this
.
[2024-10-02; Jonathan provides improved wording]
Following reflector discussion, remove the "Releases any shared state" text completely. The remaining effects imply that the state will be abandoned anyway. This makes it safe against self-assignment.
[2024-10-02; LWG telecon]
Agreed to change promise
the same way, which is safe for self-assignment
and matches what all three of libstdc++, libc++ and MSVC do today.
Ask SG1 to review.
Proposed resolution:
This wording is relative to N4988.
Modify 32.10.6 [futures.promise] as indicated:
promise& operator=(promise&& rhs) noexcept;-9- Effects:
Abandons any shared state (32.10.5 [futures.state]) and then as ifEquivalent topromise(std::move(rhs)).swap(*this)
.-10- Returns:
*this
.
Modify 32.10.10.2 [futures.task.members] as indicated:
packaged_task& operator=(packaged_task&& rhs) noexcept;-11- Effects:
(11.1) — Releases any shared state (32.10.5 [futures.state]);(11.2) — callsEquivalent topackaged_task(std::move(rhs)).swap(*this)
.-?- Returns:
*this
.