std::execution::stopped_as_error
| Defined in header <execution>
|
||
struct stopped_as_error_t { /*unspecified*/ };
|
(1) | (since C++26) |
inline constexpr stopped_as_error_t stopped_as_error{};
|
(2) | (since C++26) (customization point object) |
| Call signature |
||
execution::sender auto stopped_as_error( execution::sender auto sndr,
/* movable value */ auto err );
|
(since C++26) | |
Reports cancellation as a failure: when sndr completes with the stopped signal (it was cancelled, typically because stop was requested through its stop token), the returned sender completes with an error, err, instead. The returned sender never completes stopped itself.
A stopped completion is neither a result nor a failure, and by default it propagates upward and cancels everything that depends on it. In a coroutine it behaves like an exception that cannot be caught: it unwinds the chain of awaiting coroutines and bypasses catch (...). stopped_as_error is one of the ways to catch it: once cancellation has become an error, ordinary error handling sees it, including catch in a coroutine and upon_error and let_error in a pipeline.
Choose stopped_as_error when cancellation of this operation means that the surrounding work cannot meet its goal, so the failure should travel with the other errors, with a type of your choosing that says what was cancelled.
stopped_as_error is a pipeable sender adaptor: sndr | stopped_as_error(err) is equivalent to stopped_as_error(sndr, err).
Parameters
| sndr | - | the sender whose stopped completion is turned into an error |
| err | - | the error to complete with when sndr is cancelled; it can be of any type that is movable (for example a std::error_code, an exception object, or a std::exception_ptr)
|
Return value
A sender that behaves like sndr, except that a stopped completion of sndr is replaced by an error completion with err (the value that was passed in, moved into the operation).
Value and error completions of sndr are forwarded unchanged. The error completions of the returned sender are those of sndr plus one for the type of err.
stopped_as_error(sndr, err) is ill-formed if sndr does not satisfy execution::sender or err cannot be copied or moved into the sender (it must be a movable value, and not an array).
Customization point objects
The name execution::stopped_as_error denotes a customization point object, which is a const function object of a literal semiregular class type. The cv-unqualified version of its type is a tag type denoted as execution::stopped_as_error_t. See CustomizationPointObject for details.
Details
- Nothing runs when the sender is built. The input is started, and its completion converted, only when the returned sender is started.
erris stored in the sender and moved into the error completion when it is used. Iferris copyable, the sender can be copied and started more than once.stopped_as_errornever changes where anything runs: the returned sender completes on the execution agent on whichsndrcompleted.- It is equivalent to let_stopped with a function that returns
just_error(err); the adaptor saves writing that by hand.
Notes
- If the program only needs to know that something was cancelled, without being told why, stopped_as_optional gives an empty optional instead of an error. To turn cancellation into an ordinary value of your choice, use upon_stopped; to handle it with more asynchronous work, use let_stopped.
- The error type is yours to pick, so the cause of the cancellation can be made visible downstream: a dedicated exception type, a std::error_code (for example
std::errc::operation_canceled), or a message. - When the error finally reaches a consumer such as this_thread::sync_wait, it is converted to an exception: an std::exception_ptr is rethrown as it is, a std::error_code is thrown as a std::system_error, and any other type is thrown as an exception object of that type.
Example
A job is scheduled on an event loop while stop has already been requested through the environment's stop token, so it is cancelled before it starts. stopped_as_error turns the cancellation into an exception of a type the program defines, which ordinary catch code handles. The standard header is not available in the online compilers yet, so this link runs the example on Compiler Explorer with stdexec, the reference implementation.
#include <execution>
#include <print>
#include <stop_token>
#include <thread>
namespace ex = std::execution;
struct job_cancelled {};
// An event loop on its own thread. The destructor finishes the loop, also when
// an exception is propagating, so that the thread can be joined.
struct loop_thread
{
ex::run_loop loop;
std::jthread thread;
loop_thread() : thread([this] { loop.run(); }) {}
~loop_thread() { loop.finish(); }
};
int run_job(std::inplace_stop_source& stop_source)
{
loop_thread worker;
auto job = ex::schedule(worker.loop.get_scheduler())
| ex::then([] { return 42; });
auto sndr = ex::write_env(std::move(job),
ex::prop{ex::get_stop_token, stop_source.get_token()})
| ex::stopped_as_error(job_cancelled{});
// sync_wait throws the error as an exception, so a cancelled job ends up in the catch.
auto [result] = std::this_thread::sync_wait(std::move(sndr)).value();
return result;
}
int main()
{
std::inplace_stop_source not_stopped;
std::inplace_stop_source stopped;
stopped.request_stop();
try
{
std::println("job finished: {}", run_job(not_stopped));
std::println("job finished: {}", run_job(stopped));
}
catch (const job_cancelled&)
{
std::println("job was cancelled");
}
}
Output:
job finished: 42
job was cancelled
See also
returns a sender that maps the value channel to std::optional<std::decay_t<T>> and the stopped channel to std::nullopt(customization point object) | |
(C++26) |
chains a function onto a sender, calling it if the sender is cancelled and sending its result on as a value (customization point object) |
(C++26) |
chains a function onto a sender, calling it if the sender is cancelled and continuing with the sender it returns (customization point object) |