32 Thread support library [thread]

32.3 Stop tokens [thread.stoptoken]

32.3.1 Introduction [thread.stoptoken.intro]

This clause describes components that can be used to asynchonously request that an operation stops execution in a timely manner, typically because the result is no longer required.
Such a request is called a stop request.
stop_­source, stop_­token, and stop_­callback implement semantics of shared ownership of a stop state.
Any stop_­source, stop_­token, or stop_­callback that shares ownership of the same stop state is an associated stop_­source, stop_­token, or stop_­callback, respectively.
The last remaining owner of the stop state automatically releases the resources associated with the stop state.
A stop_­token can be passed to an operation which can either
  • actively poll the token to check if there has been a stop request, or
  • register a callback using the stop_­callback class template which will be called in the event that a stop request is made.
A stop request made via a stop_­source will be visible to all associated stop_­token and stop_­source objects.
Once a stop request has been made it cannot be withdrawn (a subsequent stop request has no effect).
Callbacks registered via a stop_­callback object are called when a stop request is first made by any associated stop_­source object.
Calls to the functions request_­stop, stop_­requested, and stop_­possible do not introduce data races.
A call to request_­stop that returns true synchronizes with a call to stop_­requested on an associated stop_­token or stop_­source object that returns true.
Registration of a callback synchronizes with the invocation of that callback.

32.3.2 Header <stop_­token> synopsis [thread.stoptoken.syn]

namespace std {
  // [stoptoken], class stop_­token
  class stop_token;

  // [stopsource], class stop_­source
  class stop_source;

  // no-shared-stop-state indicator
  struct nostopstate_t {
    explicit nostopstate_t() = default;
  };
  inline constexpr nostopstate_t nostopstate{};

  // [stopcallback], class stop_­callback
  template<class Callback>
  class stop_callback;
}

32.3.3 Class stop_­token [stoptoken]

The class stop_­token provides an interface for querying whether a stop request has been made (stop_­requested) or can ever be made (stop_­possible) using an associated stop_­source object ([stopsource]).
A stop_­token can also be passed to a stop_­callback ([stopcallback]) constructor to register a callback to be called when a stop request has been made from an associated stop_­source.
namespace std {
  class stop_token {
  public:
    // [stoptoken.cons], constructors, copy, and assignment
    stop_token() noexcept;

    stop_token(const stop_token&) noexcept;
    stop_token(stop_token&&) noexcept;
    stop_token& operator=(const stop_token&) noexcept;
    stop_token& operator=(stop_token&&) noexcept;
    ~stop_token();
    void swap(stop_token&) noexcept;

    // [stoptoken.mem], stop handling
    [[nodiscard]] bool stop_requested() const noexcept;
    [[nodiscard]] bool stop_possible() const noexcept;

    [[nodiscard]] friend bool operator==(const stop_token& lhs, const stop_token& rhs) noexcept;
    friend void swap(stop_token& lhs, stop_token& rhs) noexcept;
  };
}

32.3.3.1 Constructors, copy, and assignment [stoptoken.cons]

stop_token() noexcept;
Postconditions: stop_­possible() is false and stop_­requested() is false.
[Note
:
Because the created stop_­token object can never receive a stop request, no resources are allocated for a stop state.
— end note
]
stop_token(const stop_token& rhs) noexcept;
Postconditions: *this == rhs is true.
[Note
:
*this and rhs share the ownership of the same stop state, if any.
— end note
]
stop_token(stop_token&& rhs) noexcept;
Postconditions: *this contains the value of rhs prior to the start of construction and rhs.stop_­possible() is false.
~stop_token();
Effects: Releases ownership of the stop state, if any.
stop_token& operator=(const stop_token& rhs) noexcept;
Effects: Equivalent to: stop_­token(rhs).swap(*this).
Returns: *this.
stop_token& operator=(stop_token&& rhs) noexcept;
Effects: Equivalent to: stop_­token(std​::​move(rhs)).swap(*this).
Returns: *this.
void swap(stop_token& rhs) noexcept;
Effects: Exchanges the values of *this and rhs.

32.3.3.2 Members [stoptoken.mem]

[[nodiscard]] bool stop_requested() const noexcept;
Returns: true if *this has ownership of a stop state that has received a stop request; otherwise, false.
[[nodiscard]] bool stop_possible() const noexcept;
Returns: false if:
  • *this does not have ownership of a stop state, or
  • a stop request was not made and there are no associated stop_­source objects;
otherwise, true.

32.3.3.3 Non-member functions [stoptoken.nonmembers]

[[nodiscard]] bool operator==(const stop_token& lhs, const stop_token& rhs) noexcept;
Returns: true if lhs and rhs have ownership of the same stop state or if both lhs and rhs do not have ownership of a stop state; otherwise false.
friend void swap(stop_token& x, stop_token& y) noexcept;
Effects: Equivalent to: x.swap(y).

32.3.4 Class stop_­source [stopsource]

The class stop_­source implements the semantics of making a stop request.
A stop request made on a stop_­source object is visible to all associated stop_­source and stop_­token ([stoptoken]) objects.
Once a stop request has been made it cannot be withdrawn (a subsequent stop request has no effect).
namespace std {
  // no-shared-stop-state indicator
  struct nostopstate_t {
    explicit nostopstate_t() = default;
  };
  inline constexpr nostopstate_t nostopstate{};

  class stop_source {
  public:
    // [stopsource.cons], constructors, copy, and assignment
    stop_source();
    explicit stop_source(nostopstate_t) noexcept;

    stop_source(const stop_source&) noexcept;
    stop_source(stop_source&&) noexcept;
    stop_source& operator=(const stop_source&) noexcept;
    stop_source& operator=(stop_source&&) noexcept;
    ~stop_source();
    void swap(stop_source&) noexcept;

    // [stopsource.mem], stop handling
    [[nodiscard]] stop_token get_token() const noexcept;
    [[nodiscard]] bool stop_possible() const noexcept;
    [[nodiscard]] bool stop_requested() const noexcept;
    bool request_stop() noexcept;

    [[nodiscard]] friend bool
      operator==(const stop_source& lhs, const stop_source& rhs) noexcept;
    friend void swap(stop_source& lhs, stop_source& rhs) noexcept;
  };
}

32.3.4.1 Constructors, copy, and assignment [stopsource.cons]

stop_source();
Effects: Initialises *this to have ownership of a new stop state.
Postconditions: stop_­possible() is true and stop_­requested() is false.
Throws: bad_­alloc if memory could not be allocated for the stop state.
explicit stop_source(nostopstate_t) noexcept;
Postconditions: stop_­possible() is false and stop_­requested() is false.
[Note
:
No resources are allocated for the state.
— end note
]
stop_source(const stop_source& rhs) noexcept;
Postconditions: *this == rhs is true.
[Note
:
*this and rhs share the ownership of the same stop state, if any.
— end note
]
stop_source(stop_source&& rhs) noexcept;
Postconditions: *this contains the value of rhs prior to the start of construction and rhs.stop_­possible() is false.
~stop_source();
Effects: Releases ownership of the stop state, if any.
stop_source& operator=(const stop_source& rhs) noexcept;
Effects: Equivalent to: stop_­source(rhs).swap(*this).
Returns: *this.
stop_source& operator=(stop_source&& rhs) noexcept;
Effects: Equivalent to: stop_­source(std​::​move(rhs)).swap(*this).
Returns: *this.
void swap(stop_source& rhs) noexcept;
Effects: Exchanges the values of *this and rhs.

32.3.4.2 Members [stopsource.mem]

[[nodiscard]] stop_token get_token() const noexcept;
Returns: stop_­token() if stop_­possible() is false; otherwise a new associated stop_­token object.
[[nodiscard]] bool stop_possible() const noexcept;
Returns: true if *this has ownership of a stop state; otherwise, false.
[[nodiscard]] bool stop_requested() const noexcept;
Returns: true if *this has ownership of a stop state that has received a stop request; otherwise, false.
bool request_stop() noexcept;
Effects: If *this does not have ownership of a stop state, returns false.
Otherwise, atomically determines whether the owned stop state has received a stop request, and if not, makes a stop request.
The determination and making of the stop request are an atomic read-modify-write operation ([intro.races]).
If the request was made, the callbacks registered by associated stop_­callback objects are synchronously called.
If an invocation of a callback exits via an exception then terminate is called ([except.terminate]).
[Note
:
A stop request includes notifying all condition variables of type condition_­variable_­any temporarily registered during an interruptible wait ([thread.condvarany.intwait]).
— end note
]
Postconditions: stop_­possible() is false or stop_­requested() is true.
Returns: true if this call made a stop request; otherwise false.

32.3.4.3 Non-member functions [stopsource.nonmembers]

[[nodiscard]] friend bool operator==(const stop_source& lhs, const stop_source& rhs) noexcept;
Returns: true if lhs and rhs have ownership of the same stop state or if both lhs and rhs do not have ownership of a stop state; otherwise false.
friend void swap(stop_source& x, stop_source& y) noexcept;
Effects: Equivalent to: x.swap(y).

32.3.5 Class template stop_­callback [stopcallback]

namespace std {
  template<class Callback>
  class stop_callback {
  public:
    using callback_type = Callback;

    // [stopcallback.cons], constructors and destructor
    template<class C>
      explicit stop_callback(const stop_token& st, C&& cb)
        noexcept(is_nothrow_constructible_v<Callback, C>);
    template<class C>
      explicit stop_callback(stop_token&& st, C&& cb)
        noexcept(is_nothrow_constructible_v<Callback, C>);
    ~stop_callback();

    stop_callback(const stop_callback&) = delete;
    stop_callback(stop_callback&&) = delete;
    stop_callback& operator=(const stop_callback&) = delete;
    stop_callback& operator=(stop_callback&&) = delete;

  private:
    Callback callback;      // exposition only
  };

  template<class Callback>
    stop_callback(stop_token, Callback) -> stop_callback<Callback>;
}
Mandates: stop_­callback is instantiated with an argument for the template parameter Callback that satisfies both invocable and destructible.
Preconditions: stop_­callback is instantiated with an argument for the template parameter Callback that models both invocable and destructible.

32.3.5.1 Constructors and destructor [stopcallback.cons]

template<class C> explicit stop_callback(const stop_token& st, C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>); template<class C> explicit stop_callback(stop_token&& st, C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>);
Constraints: Callback and C satisfy constructible_­from<Callback, C>.
Preconditions: Callback and C model constructible_­from<Callback, C>.
Effects: Initializes callback with std​::​forward<C>(cb).
If st.stop_­requested() is true, then std​::​forward<Callback>(callback)() is evaluated in the current thread before the constructor returns.
Otherwise, if st has ownership of a stop state, acquires shared ownership of that stop state and registers the callback with that stop state such that std​::​forward<Callback>(callback)() is evaluated by the first call to request_­stop() on an associated stop_­source.
Remarks: If evaluating std​::​forward<Callback>(callback)() exits via an exception, then terminate is called ([except.terminate]).
Throws: Any exception thrown by the initialization of callback.
~stop_callback();
Effects: Unregisters the callback from the owned stop state, if any.
The destructor does not block waiting for the execution of another callback registered by an associated stop_­callback.
If callback is concurrently executing on another thread, then the return from the invocation of callback strongly happens before ([intro.races]) callback is destroyed.
If callback is executing on the current thread, then the destructor does not block ([defns.block]) waiting for the return from the invocation of callback.
Releases ownership of the stop state, if any.