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.
run_loop should not have a set_error completionSection: 33.12.1.1 [exec.run.loop.general] Status: New Submitter: Eric Niebler Opened: 2025-11-16 Last modified: 2026-01-16
Priority: 2
View all issues with New status.
Discussion:
When run_loop was proposed, the only implementation we had synchronized with a mutex and a condition variable.
Operations on those can theoretically throw exceptions, so run_loop got a set_error_t(exception_ptr)
completion signature.
run_loop has been found. Atomic operations cannot fail with an exception,
so an atomic run_loop can never complete with an error.
[2026-01-16; Reflector poll.]
Set priority to 2 after reflector poll.
"Very helpful to know if something completes without exceptions, it affects whether completions on some types of scheduler are correct."
"Want confirmation from LEWG as run() and finish() have narrow contracts."
Proposed resolution:
This wording is relative to N5014.
[Drafting note: It should be pointed out that member function
finishshould benoexceptfor other reasons as well, see LWG 4215(i).]
Modify 33.12.1.1 [exec.run.loop.general], class run_loop synopsis, as indicated:
namespace std::execution {
class run_loop {
// 33.12.1.2 [exec.run.loop.types], associated types
class run-loop-scheduler; // exposition only
class run-loop-sender; // exposition only
struct run-loop-opstate-base { // exposition only
virtual void execute() noexcept = 0; // exposition only
run_loop* loop; // exposition only
run-loop-opstate-base* next; // exposition only
};
template<class Rcvr>
using run-loop-opstate = unspecified; // exposition only
// 33.12.1.4 [exec.run.loop.members], member functions
run-loop-opstate-base* pop-front() noexcept; // exposition only
void push-back(run-loop-opstate-base*) noexcept; // exposition only
public:
// 33.12.1.3 [exec.run.loop.ctor], constructor and destructor
run_loop() noexcept;
run_loop(run_loop&&) = delete;
~run_loop();
// 33.12.1.4 [exec.run.loop.members] member functions
run-loop-scheduler get_scheduler() noexcept;
void run() noexcept;
void finish() noexcept;
};
}
Modify 33.12.1.2 [exec.run.loop.types] as indicated:
class run-loop-sender;-5-
run-loop-senderis an exposition-only type that satisfies sender.completion_signatures_of_t<runloop-sender>iscompletion_signatures<set_value_t(),set_error_t(exception_ptr),set_stopped_t()>[…]
-9- Letobe a non-const lvalue of typerun-loop-opstate<Rcvr>, and letREC(o)be a non-const lvalue reference to an instance of typeRcvrthat was initialized with the expressionrcvrpassed to the invocation of connect that returnedo. Then:
- (9.1) — […]
- (9.2) — […]
- (9.3) — The expression
start(o)is equivalent to:try {o.loop->push-back(addressof(o));} catch(...) { set_error(std::move(REC(o)), current_exception()); }
Modify 33.12.1.4 [exec.run.loop.members] as indicated:
run-loop-opstate-base* pop-front() noexcept;-1- Effects: Blocks (3.6 [defns.block]) until one of the following conditions is
true: […]void push-back(run-loop-opstate-base* item) noexcept;-2- Effects: Adds
-3- Synchronization: This operation synchronizes with theitemto the back of the queue and incrementscountby1.pop-frontoperation that obtainsitem.run-loop-scheduler get_scheduler() noexcept;-4- Returns: An instance of
run-loop-schedulerthat can be used to schedule work onto thisrun_loopinstance.void run() noexcept;-5- Preconditions: is either
-6- Effects: Ifstartingorfinishing.stateisstarting, sets thestatetorunning, otherwise leavesstateunchanged. Then, equivalent to:while (auto* op = pop-front()) { op->execute(); }-7- Remarks: When
statechanges, it does so without introducing data races.void finish() noexcept;-8- Preconditions:
-9- Effects: Changesstateis eitherstartingorrunning.statetofinishing. -10- Synchronization:finishsynchronizes with thepop-frontoperation that returnsnullptr.