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

4504. Wording problem in {simple_}counting_scope

Section: 33.14.2.2.3 [exec.simple.counting.mem] Status: Immediate Submitter: Ian Petersen Opened: 2025-12-24 Last modified: 2026-03-26

Priority: 2

View all issues with Immediate status.

Discussion:

I discovered while implementing P3149 + P3815 in NVIDIA's stdexec that there's a bug in 33.14.2.2.3 [exec.simple.counting.mem], paragraph 9. The status quo says:

template<class State>
  bool start-join-sender(State& st) noexcept;

-9- Effects: If state is

The problem is that if either bullet 9.2 or 9.3 applies when count happens to be zero then there's nothing to guarantee that st.complete() is ever invoked, leading to deadlock unless another sender is associated and disassociated with the scope (because the subsequent disassociation will trigger the join-sender's completion).

One fix is to change bullet 9.1 of the Effects element such that start-join-sender changes state to joined and returns true if count is zero, regardless of the value of state; bullets 9.2 and 9.3 can be left alone. I might add a note that points out that, when state is unused, unused-and-closed, or joined, count must be zero.

Another fix would be to delete bullet 9.1, to modify bullets 9.2 and 9.3 by adding "and count is greater than zero", and to add a trailing bullet that says "otherwise, changes state to joined and returns true."

[2026-02-18; Reflector poll.]

Set priority to 2 after reflector poll.

[2026-03-25; Tomasz provides updated wording.]

[Croydon 2026-03-26; move to Immediate.]

Proposed resolution:

This wording is relative to N5032.

  1. Modify 33.14.2.2.3 [exec.simple.counting.mem] as indicated:

    template<class State>
      bool start-join-sender(State& st) noexcept;
    

    -9- Effects: If count is zero, then changes state to joined and returns true. Otherwise, if state is:

    • (9.1) — unused, unused-and-closed, or joined, then changes state to joined and returns true;

    • (9.2) — open or open-and-joining, then changes state to open-and-joining, registers st with *this and returns false;

    • (9.3) — closed or closed-and-joining, then changes state to closed-and-joining, registers st with *this and returns false.