Title
jthread::operator=(jthread&&) postconditions are unimplementable under self-assignment
Status
c++23
Section
[thread.jthread.cons]
Submitter
Nicole Mazzuca

Created on 2022-09-22.00:00:00 last changed 12 months ago

Messages

Date: 2022-11-17.00:42:33

Proposed resolution:

This wording is relative to N4917.

  1. Modify [thread.jthread.cons] as indicated:

    jthread& operator=(jthread&& x) noexcept;

    -12- Effects: If &x == this is true, there are no effects. Otherwise, if joinable() is true, calls request_stop() and then join(). Assigns, then assigns the state of x to *this and sets x to a default constructed state.

    -13- Postconditions: x.get_id() == id() and get_id() returns the value of x.get_id() prior to the assignment. ssource has the value of x.ssource prior to the assignment and x.ssource.stop_possible() is false.

    -14- Returns: *this.

Date: 2022-11-12.00:00:00

[ 2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Voting → WP. ]

Date: 2022-09-15.00:00:00

[ 2022-09-29; Reflector poll ]

Set status to Tentatively Ready after five votes in favour during reflector poll.

Date: 2022-09-15.00:00:00

[ 2022-09-28; Jonathan provides wording ]

Date: 2022-09-15.00:00:00

[ 2022-09-28; Reflector poll ]

Set priority to 3 after reflector poll.

Date: 2022-09-22.00:00:00

In the Postconditions element of jthread& jthread::operator=(jthread&&) ([thread.jthread.cons] p13), we have the following:

Postconditions: x.get_id() == id(), and get_id() returns the value of x.get_id() prior to the assignment. ssource has the value of x.ssource prior to the assignment and x.ssource.stop_possible() is false.

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 implement jthread.

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.

History
Date User Action Args
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2022-11-17 00:42:33adminsetmessages: + msg13081
2022-11-17 00:42:33adminsetstatus: voting -> wp
2022-11-08 03:46:49adminsetstatus: ready -> voting
2022-10-12 14:38:06adminsetmessages: + msg12849
2022-10-12 14:38:06adminsetstatus: open -> ready
2022-09-28 16:24:25adminsetmessages: + msg12827
2022-09-28 16:24:25adminsetmessages: + msg12826
2022-09-28 16:24:25adminsetmessages: + msg12825
2022-09-28 16:24:25adminsetstatus: new -> open
2022-09-22 00:00:00admincreate