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.

4347. task's stop source is always created

Section: 33.13.6.5 [task.promise] Status: New Submitter: Dietmar Kühl Opened: 2025-09-01 Last modified: 2026-03-23

Priority: 2

View other active issues in [task.promise].

View all other issues in [task.promise].

View all issues with New status.

Discussion:

Addresses US 257-382

The type task<...>::promise_type has exposition-only members source and token. These can be interpreted as always existing which would be a performance issue for former and an unnecessary constraints for the latter (because stop tokens aren't required to be default constructible).

The intent is that the stop token obtained from the get_stop_token query of the receiver's environment is used. Only if this type is different from the task's stop_token_type a stop source of type stop_source_type needs to be created when the get_stop_token query is used on the promise type's environment. The stop token doesn't need to be stored at all: it can either be obtained from the receiver's environment or from the stop source. The fix is to show the stop source as an optionally present member of of the operation state and it should be of type std::optional<stop_source_type> to imply that it is only created when accessed.

[2025-10-17; Reflector poll.]

Set priority to 2 after reflector poll.

Proposed resolution:

This wording is relative to N5032.

  1. Change the synopsis in 33.13.6.4 [task.state] to include an optional<stop_source_type> member and an exposition-only function to get a stop token:

    namespace std::execution {
      template<class T, class Environment>
      template<receiver Rcvr>
      class task<T, Environment>::state {           // exposition only
      public:
        using operation_state_concept = operation_state_t;
    
        template<class R>
          state(coroutine_handle<promise_type> h, R&& rr);
    
        ~state();
    
        void start() & noexcept;
    
        stop_token_type get-stop-token(); // exposition only
    
      private:
        using own-env-t = see below;                // exposition only
        coroutine_handle<promise_type> handle;      // exposition only
        remove_cvref_t<Rcvr>           rcvr;        // exposition only
        optional<stop_source_type>     source;      // exposition only
        own-env-t                      own-env;     // exposition only
        Environment                    environment; // exposition only
      };
    }
    
  2. In 33.13.6.4 [task.state] change paragraph 4 into two paragraphs: one for start() and one for get-stop-token() such that the initialization of source is in the second paragraph:

    void start() & noexcept;
    

    -4- Effects: Let prom be the object handle.promise(). Associates STATE(prom), RCVR(prom), and SCHED(prom) with *this as follows:

    -4.1- -- STATE(prom) is *this.

    -4.2- -- RCVR(prom) is rcvr.

    -4.3- -- SCHED(prom) is the object initialized with scheduler_type(get_scheduler(get_env(rcvr))) if that expression is valid and scheduler_type() otherwise. If neither of these expressions is valid, the program is ill-formed.

    Let st be get_stop_token(get_env(rcvr)). Initializes prom.token and prom.source such that

    -4.4- -- prom.token.stop_requested() returns st.stop_requested();

    -4.5- -- prom.token.stop_possible() returns st.stop_possible(); and

    -4.6- -- for types Fn and Init such that both invocable<Fn> and constructible_from<Fn, Init> are modeled, stop_token_type::callback_type<Fn> models stoppable-callback-for<Fn, stop_token_type, Init>.

    -5- After that invokes handle.resume().

    stop_token_type get-stop-token();
    

    -6- Effects: If same_as<decltype(declval<stop_source_type>().get_token()), decltype(get_stop_token(get_env(rcvr)))> is true returns get_stop_token(get_env(rcvr)). Otherwise, if source.has_value() is false, initializes source such that

    • -6.1- -- source->stop_requested() returns get_stop_token(get_env(rcvr))->stop_requested(); and
    • -6.2- -- source->stop_possible() returns get_stop_token(get_env(rcvr))->stop_possible()

    Finally, returns source->get_token().

  3. Remove token and source from the synopsis of promise_type in 33.13.6.5 [task.promise]:

    namespace std::execution {
      template<class T, class Environment>
      class task<T, Environment>::promise_type {
      public:
        ...
    
      private:
        using error-variant = see below;    // exposition only
    
        allocator_type    alloc;            // exposition only
        stop_source_type  source;           // exposition only
        stop_token_type   token;            // exposition only
        optional<T>       result;           // exposition only; present only if is_void_v<T> is false
        error-variant     errors;           // exposition only
      };
    }
    
  4. Change the specification of get_env in 33.13.6.5 [task.promise] paragraph 16.3 to use STATE(*this).get-stop-token():

    unspecified get_env() const noexcept;
    

    -16- Returns: An object env such that queries are forwarded as follows:

    -16.1- -- env.query(get_scheduler) returns scheduler_type(SCHED(*this)).

    -16.2- -- env.query(get_allocator) returns alloc.

    -16.3- -- env.query(get_stop_token) returns tokenSTATE(*this).get-stop-token().

    -16.4- -- For any other query q and arguments a... a call to env.query(q, a...) returns STATE(*this).environmentienvironment.query(q, a...) if this expression is well-formed and forwarding_query(q) is well-formed and is true. Otherwise env.query(q, a...) is ill-formed.