A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/cplusplus/sender-receiver/issues/329 below:

potentially throwing `run_loop::finish` can cause `sync_wait` to call `terminate` or hang · Issue #329 · cplusplus/sender-receiver · GitHub

run_loop::finish puts the run_loop into the finishing state so that the next time the work queue is empty, run_loop::run will return instead of waiting for more work.

calling .finish() on a run_loop instance can potentially throw (finish() is not marked noexcept). that is because one valid implementation involves acquiring a lock on a std::mutex -- a potentially throwing operation.

but failing to put the run_loop into the finishing state is problematic in the same way that a failing destructor is problematic: shutdown and clean-up code depends on it succeeding.

consider sync_wait's use of run_loop:

sync-wait-state<Sndr> state;
auto op = connect(sndr, sync-wait-receiver<Sndr>{&state});
start(op);

state.loop.run();
if (state.error) {
  rethrow_exception(std::move(state.error));
}
return std::move(state.result);

it is the job of sync-wait-receiver to put the run_loop into the finishing state so that the invocation of state.loop.run() will return. it does that in its completion functions, like so:

void set_stopped() && noexcept;

Effects: Equivalent to state->loop.finish().

here we are not handling the fact that state->loop.finish() is potentially throwing. given that this function is noexcept, this will lead to the application getting terminated. not good.

but even if we handle the exception and save it into state.result to be rethrown later, we still have a problem. since run_loop::finish() threw, the run_loop has not been placed into the finishing state. that means that state.loop.run() will never return, and sync_wait will hang forever.

simply put, run_loop::finish() has to be noexcept. the implementation must find a way to put the run_loop into the finishing state. if it cannot, it should terminate. throwing an exception and foisting the problem on the caller -- who has no recourse -- is simply wrong.

Proposed Resolution:

Change run_loop::finish() no-throw. in [exec.run.loop.general], change the definition of the run_loop class as follows:

 namespace std::execution {
   class run_loop {
     // [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() = 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
 
     // [exec.run.loop.members], member functions
     run-loop-opstate-base* pop-front();                         // exposition only
     void push-back(run-loop-opstate-base*);                     // exposition only
 
   public:
     // [exec.run.loop.ctor], constructor and destructor
     run_loop() noexcept;
     run_loop(run_loop&&) = delete;
     ~run_loop();
 
     // [exec.run.loop.members], member functions
     run-loop-scheduler get_scheduler();
     void run();
-    void finish();
+    void finish() noexcept;
   };
 }

change [exec.run.loop.mempers] p8 as follows:

-void finish();
+void finish() noexcept;
  1. Preconditions: state is either starting or running.
  2. Effects: Changes state to finishing.
  3. Synchronization: finish synchronizes with the pop-front operation that returns nullptr.

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4