32 Concurrency support library [thread]
Subclause [thread.
stoptoken] describes components that can be used
to asynchronously request that an operation stops execution in a timely manner,
typically because the result is no longer required
. An object of a type that models
stoppable_token
can be passed to an operation that can either
- actively poll the token to check if there has been a stop request, or
- register a callback that
will be called in the event that a stop request is made.
Once a stop request has been made it cannot be withdrawn
(a subsequent stop request has no effect)
.The types
stop_source and
stop_token and
the class template
stop_callback implement
the semantics of shared ownership of a stop state
. The last remaining owner of the stop state automatically releases
the resources associated with the stop state
.An object of type
inplace_stop_source
is the sole owner of its stop state
. An object of type
inplace_stop_token or
of a specialization of the class template
inplace_stop_callback
does not participate in ownership of its associated stop state
. [
Note 1:
They are for use when all uses of the associated token and callback objects
are known to nest within the lifetime of the
inplace_stop_source object
. —
end note]
Let
t and
u be distinct, valid objects of type
Token
that reference the same logical stop state;
let
init be an expression such that
same_as<decltype(init), Initializer> is
true; and
let
SCB denote the type
stop_callback_for_t<Token, CallbackFn>.The concept
stoppable-callback-for<CallbackFn, Token, Initializer>
is modeled only if:
The following concepts are modeled:
An object of type
SCB has
an associated callback function of type
CallbackFn. Let
scb be an object of type
SCB and
let
callback_fn denote
scb's' associated callback function
. Direct-non-list-initializing
scb from
arguments
t and
init
shall execute a
stoppable callback registration as follows:
- If t.stop_possible() is true:
callback_fn shall be direct-initialized with
init. Construction of
scb shall only throw exceptions
thrown by the initialization of
callback_fn from
init.The callback invocation
std::forward<CallbackFn>(callback_fn)()
shall be registered with
t's associated stop state as follows:
If
t.stop_requested() evaluates to
false
at the time of registration,
the callback invocation is added to the stop state's list of callbacks
such that
std::forward<CallbackFn>(
callback_fn)() is evaluated
if a stop request is made on the stop state
.Otherwise,
std::forward<CallbackFn>(callback_fn)()
shall be immediately evaluated
on the thread executing
scb's constructor, and
the callback invocation shall not be added to the list of callback invocations
.
If the callback invocation was added to stop state's list of callbacks,
scb shall be associated with the stop state
.
[
Note 1:
If
t.stop_possible() is
false,
there is no requirement
that the initialization of
scb
causes the initialization of
callback_fn. —
end note]
Destruction of
scb shall execute
a
stoppable callback deregistration as follows (in order):
If the constructor of
scb did not register
a callback invocation with
t's stop state,
then the stoppable callback deregistration shall have no effect
other than destroying
callback_fn if it was constructed
.Otherwise, the invocation of
callback_fn shall be removed
from the associated stop state
.If
callback_fn is concurrently executing on another thread,
then the stoppable callback deregistration shall block (
[defns.block])
until the invocation of
callback_fn returns
such that the return from the invocation of
callback_fn
strongly happens before (
[intro.races])
the destruction of
callback_fn.If
callback_fn is executing on the current thread,
then the destructor shall not block
waiting for the return from the invocation of
callback_fn.A stoppable callback deregistration shall not block
on the completion of the invocation of some other callback
registered with the same logical stop state
.The stoppable callback deregistration shall destroy
callback_fn.
The
stoppable_token concept checks
for the basic interface of a stop token
that is copyable and allows polling to see if stop has been requested and
also whether a stop request is possible
. template<template<class> class>
struct check-type-alias-exists;
template<class Token>
concept stoppable_token =
requires (const Token tok) {
typename check-type-alias-exists<Token::template callback_type>;
{ tok.stop_requested() } noexcept -> same_as<bool>;
{ tok.stop_possible() } noexcept -> same_as<bool>;
{ Token(tok) } noexcept;
} &&
copyable<Token> &&
equality_comparable<Token>;
template<class Token>
concept unstoppable_token =
stoppable_token<Token> &&
requires (const Token tok) {
requires bool_constant<(!tok.stop_possible())>::value;
};
An object whose type models
stoppable_token
has at most one associated logical stop state
. Let
SP be an evaluation of
t.stop_possible()
that is
false, and
let SR be an evaluation of
t.stop_requested() that is
true.The type
Token models
stoppable_token only if:
Any evaluation of
u.stop_possible() or
u.stop_requested()
that happens after (
[intro.races])
SP is
false.Any evaluation of
u.stop_possible() or
u.stop_requested()
that happens after
SR is
true.If
t is disengaged,
evaluations of
t.stop_possible() and
t.stop_requested()
are
false.If
t and
u reference the same stop state, or
if both
t and
u are disengaged,
t == u is
true; otherwise, it is
false.
An object
whose type models the exposition-only
stoppable-source concept
can be queried
whether stop has been requested (
stop_requested) and
whether stop is possible (
stop_possible)
. It is a factory for associated stop tokens (
get_token), and
a stop request can be made on it (
request_stop)
. It maintains a list of registered stop callback invocations
that it executes when a stop request is first made
. template<class Source>
concept stoppable-source =
requires (Source& src, const Source csrc) {
{ csrc.get_token() } -> stoppable_token;
{ csrc.stop_possible() } noexcept -> same_as<bool>;
{ csrc.stop_requested() } noexcept -> same_as<bool>;
{ src.request_stop() } -> same_as<bool>;
};
An object whose type models
stoppable-source has
at most one associated logical stop state
. If it has no associated stop state, it is said to be disengaged
. s.stop_possible() and
s.stop_requested() shall be
false. If
t is disengaged,
t.get_token() shall return a disengaged stop token;
otherwise, it shall return
a stop token that is associated with the stop state of
t. Calls to the member functions
request_stop,
stop_requested, and
stop_possible and
similarly named member functions
on associated
stoppable_token objects
do not introduce data races
. A call to
request_stop that returns
true synchronizes with
a call to
stop_requested on
an associated
stoppable_token or
stoppable-source object
that returns
true. Registration of a callback synchronizes with the invocation of that callback
.If the
stoppable-source is disengaged,
request_stop shall have no effect and return
false. A stop request operation determines
whether the stop state has received a stop request, and
if not, makes a stop request
. The determination and making of the stop request shall happen atomically,
as-if by a read-modify-write operation (
[intro.races])
. If the request was made,
the stop state's registered callback invocations shall be
synchronously executed
. If an invocation of a callback exits via an exception
then
terminate shall be invoked (
[except.terminate])
. [
Note 2:
No constraint is placed on the order
in which the callback invocations are executed
. —
end note]
request_stop shall return
true if a stop request was made, and
false otherwise
. After a call to
request_stop either
a call to
stop_possible shall return
false or
a call to
stop_requested shall return
true. [
Note 3:
A stop request includes notifying
all condition variables of type
condition_variable_any
temporarily registered during
an interruptible wait (
[thread.condvarany.intwait])
. —
end note]
It shares ownership of its stop state, if any,
with its associated
stop_source object (
[stopsource]) and
any
stop_token objects to which it compares equal
. namespace std {
class stop_token {
public:
template<class CallbackFn>
using callback_type = stop_callback<CallbackFn>;
stop_token() noexcept = default;
void swap(stop_token&) noexcept;
bool stop_requested() const noexcept;
bool stop_possible() const noexcept;
bool operator==(const stop_token& rhs) noexcept = default;
private:
shared_ptr<unspecified> stop-state;
};
}
stop-state refers to the
stop_token's associated stop state
. A
stop_token object is disengaged when
stop-state is empty
.void swap(stop_token& rhs) noexcept;
Effects: Equivalent to:
stop-state.swap(rhs.stop-state);
bool stop_requested() const noexcept;
Returns:
true if
stop-state refers to a stop state
that has received a stop request;
otherwise,
false. bool stop_possible() const noexcept;
Returns:
false if
- *this is disengaged, or
- a stop request was not made
and there are no associated stop_source objects;
otherwise,
true. namespace std {
class stop_source {
public:
stop_source();
explicit stop_source(nostopstate_t) noexcept {}
void swap(stop_source&) noexcept;
stop_token get_token() const noexcept;
bool stop_possible() const noexcept;
bool stop_requested() const noexcept;
bool request_stop() noexcept;
bool operator==(const stop_source& rhs) noexcept = default;
private:
shared_ptr<unspecified> stop-state;
};
}
stop-state refers to the
stop_source's associated stop state
. A
stop_source object is disengaged when
stop-state is empty
.Effects: Initializes
stop-state with a pointer to a new stop state
. Postconditions:
stop_possible() is
true
and
stop_requested() is
false. Throws:
bad_alloc if memory cannot be allocated for the stop state
. void swap(stop_source& rhs) noexcept;
Effects: Equivalent to:
stop-state.swap(rhs.stop-state);
stop_token get_token() const noexcept;
Returns:
stop_token() if
stop_possible() is
false;
otherwise a new associated
stop_token object;
i.e., its
stop-state member is equal to
the
stop-state member of
*this. bool stop_possible() const noexcept;
Returns:
stop-state != nullptr. bool stop_requested() const noexcept;
Returns:
true if
stop-state refers to a stop state
that has received a stop request;
otherwise,
false. bool request_stop() noexcept;
namespace std {
template<class CallbackFn>
class stop_callback {
public:
using callback_type = CallbackFn;
template<class Initializer>
explicit stop_callback(const stop_token& st, Initializer&& init)
noexcept(is_nothrow_constructible_v<CallbackFn, Initializer>);
template<class Initializer>
explicit stop_callback(stop_token&& st, Initializer&& init)
noexcept(is_nothrow_constructible_v<CallbackFn, Initializer>);
~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:
CallbackFn callback-fn;
};
template<class CallbackFn>
stop_callback(stop_token, CallbackFn) -> stop_callback<CallbackFn>;
}
Mandates:
stop_callback is instantiated with an argument for the
template parameter
CallbackFn
that satisfies both
invocable
and
destructible. The exposition-only
callback-fn member is
the associated callback function (
[stoptoken.concepts]) of
stop_callback<
CallbackFn> objects
. template<class Initializer>
explicit stop_callback(const stop_token& st, Initializer&& init)
noexcept(is_nothrow_constructible_v<CallbackFn, Initializer>);
template<class Initializer>
explicit stop_callback(stop_token&& st, Initializer&& init)
noexcept(is_nothrow_constructible_v<CallbackFn, Initializer>);
Effects: Initializes
callback-fn with
std::forward<Initializer>(init)
and executes a stoppable callback registration (
[stoptoken.concepts])
. If a callback is registered with
st's shared stop state,
then
*this acquires shared ownership of that stop state
.Effects: Executes a stoppable callback deregistration (
[stoptoken.concepts]) and
releases ownership of the stop state, if any
. It provides a stop token interface,
but also provides static information
that a stop is never possible nor requested
. namespace std {
class never_stop_token {
struct callback-type {
explicit callback-type(never_stop_token, auto&&) noexcept {}
};
public:
template<class>
using callback_type = callback-type;
static constexpr bool stop_requested() noexcept { return false; }
static constexpr bool stop_possible() noexcept { return false; }
bool operator==(const never_stop_token&) const = default;
};
}
It references the stop state of
its associated
inplace_stop_source object (
[stopsource.inplace]),
if any
. namespace std {
class inplace_stop_token {
public:
template<class CallbackFn>
using callback_type = inplace_stop_callback<CallbackFn>;
inplace_stop_token() = default;
bool operator==(const inplace_stop_token&) const = default;
bool stop_requested() const noexcept;
bool stop_possible() const noexcept;
void swap(inplace_stop_token&) noexcept;
private:
const inplace_stop_source* stop-source = nullptr;
};
}
void swap(inplace_stop_token& rhs) noexcept;
Effects: Exchanges the values of
stop-source and
rhs.stop-source. bool stop_requested() const noexcept;
Effects: Equivalent to:
return stop-source != nullptr && stop-source->stop_requested();
[
Note 1:
As specified in
[basic.life],
the behavior of
stop_requested is undefined
unless the call strongly happens before the start of
the destructor of the associated
inplace_stop_source object, if any
. —
end note]
stop_possible() const noexcept;
Returns:
stop-source != nullptr. [
Note 2:
As specified in
[basic.stc.general],
the behavior of
stop_possible is implementation-defined
unless the call strongly happens before
the end of the storage duration of
the associated
inplace_stop_source object, if any
. —
end note]
namespace std {
class inplace_stop_source {
public:
constexpr inplace_stop_source() noexcept;
inplace_stop_source(inplace_stop_source&&) = delete;
inplace_stop_source(const inplace_stop_source&) = delete;
inplace_stop_source& operator=(inplace_stop_source&&) = delete;
inplace_stop_source& operator=(const inplace_stop_source&) = delete;
~inplace_stop_source();
constexpr inplace_stop_token get_token() const noexcept;
static constexpr bool stop_possible() noexcept { return true; }
bool stop_requested() const noexcept;
bool request_stop() noexcept;
};
}
constexpr inplace_stop_source() noexcept;
Effects: Initializes a new stop state inside
*this. Postconditions:
stop_requested() is
false. constexpr inplace_stop_token get_token() const noexcept;
Returns: A new associated
inplace_stop_token object
whose
stop-source member is equal to
this. bool stop_requested() const noexcept;
Returns:
true if the stop state inside
*this
has received a stop request; otherwise,
false. bool request_stop() noexcept;
Postconditions:
stop_requested() is
true. namespace std {
template<class CallbackFn>
class inplace_stop_callback {
public:
using callback_type = CallbackFn;
template<class Initializer>
explicit inplace_stop_callback(inplace_stop_token st, Initializer&& init)
noexcept(is_nothrow_constructible_v<CallbackFn, Initializer>);
~inplace_stop_callback();
inplace_stop_callback(inplace_stop_callback&&) = delete;
inplace_stop_callback(const inplace_stop_callback&) = delete;
inplace_stop_callback& operator=(inplace_stop_callback&&) = delete;
inplace_stop_callback& operator=(const inplace_stop_callback&) = delete;
private:
CallbackFn callback-fn;
};
template<class CallbackFn>
inplace_stop_callback(inplace_stop_token, CallbackFn)
-> inplace_stop_callback<CallbackFn>;
}
For an
inplace_stop_callback<CallbackFn> object,
the exposition-only
callback-fn member is
its associated callback function (
[stoptoken.concepts])
. template<class Initializer>
explicit inplace_stop_callback(inplace_stop_token st, Initializer&& init)
noexcept(is_nothrow_constructible_v<CallbackFn, Initializer>);
Effects: Initializes
callback-fn with
std::forward<Initializer>(init)
and executes a stoppable callback registration (
[stoptoken.concepts])
. ~inplace_stop_callback();