jthread::operator=(jthread&&)
postconditions are unimplementable under self-assignmentSection: 32.4.4.2 [thread.jthread.cons] Status: C++23 Submitter: Nicole Mazzuca Opened: 2022-09-22 Last modified: 2023-11-22
Priority: 3
View all issues with C++23 status.
Discussion:
In the Postconditions element of jthread& jthread::operator=(jthread&&)
(32.4.4.2 [thread.jthread.cons] p13), we have the following:
Postconditions:
x.get_id() == id()
, andget_id()
returns the value ofx.get_id()
prior to the assignment.ssource
has the value ofx.ssource
prior to the assignment andx.ssource.stop_possible()
isfalse
.
Assume j
is a joinable jthread
. Then, j = std::move(j);
results in the following post-conditions:
Let old_id = j.get_id()
(and since j
is joinable, old_id != id()
)
Since x.get_id() == id()
, j.get_id() == id()
Since get_id() == old_id
, j.get_id() == old_id
Thus, id() == j.get_id() == old_id
, and old_id != id()
, which is a contradiction.
One can see that these postconditions are therefore unimplementable.
Two standard libraries – the MSVC STL and libstdc++ – currently implementjthread
.
The MSVC STL chooses to follow the letter of the Effects element, which results in unfortunate behavior.
It first request_stop()
s, then join()
s; then, it assigns the values over. This results in
j.get_id() == id()
– this means that std::swap(j, j)
stops and joins j
.
libstdc++ chooses instead to implement this move assignment operator via the move-swap idiom.
This results in j.get_id() == old_id
, and std::swap(j, j)
is a no-op.
It is the opinion of the issue writer that libstdc++'s choice is the correct one, and should be
taken into the standard.
[2022-09-28; Reflector poll]
Set priority to 3 after reflector poll.
[2022-09-28; Jonathan provides wording]
[2022-09-29; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
[2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4917.
Modify 32.4.4.2 [thread.jthread.cons] as indicated:
jthread& operator=(jthread&& x) noexcept;-12- Effects: If
&x == this
istrue
, there are no effects. Otherwise, ifjoinable()
istrue
, callsrequest_stop()
and thenjoin()
. Assigns, then assigns the state ofx
to*this
and setsx
to a default constructed state.-13- Postconditions:
x.get_id() == id()
andget_id()
returns the value ofx.get_id()
prior to the assignment.ssource
has the value ofx.ssource
prior to the assignmentand.x.ssource.stop_possible()
isfalse
-14- Returns:
*this
.