Reputation: 17
I am executing n task using std::for_each and these tasks can be canceled. So for doing that I have a flag that is set to true if tasks to be canceled which in turn throws some exception in the task's code. And it works fine if I use normal std::for_each, but it aborts if I use any of std execution_policy. Is there a way to stop my code from aborting?
#include <execution>
#include <array>
#include <chrono>
#include <exception>
using namespace std::chrono_literals;
auto main(int argc, char* argv[]) -> int {
std::array<int, 5> x {1, 2, 3, 4, 5};
std::atomic<bool> toBeCancelled = true;
std::for_each(std::execution::par, std::begin(x), std::end(x), [&](const int& x) {
std::this_thread::sleep_for(2s);
if (toBeCancelled)
throw std::runtime_error("taskCancelled");
});
return 0;
}
The number of tasks can be 1000-10000. I usually don't use exceptions, instead I cover major code in "if condition". but here task count is so large, I thought running code outside of "if condition" is not worth it.
Upvotes: 1
Views: 556
Reputation: 141523
From cppreference execution policies:
During the execution of a parallel algorithm with any of these execution policies, if the invocation of an element access function exits via an uncaught exception, std::terminate is called, but the implementations may define additional execution policies that handle exceptions differently.
So if it throws, your program will terminate.
How to handle exception for parallel std algorithms
Then cppreference std::thread has the answer to your question:
[...] function may communicate its return value or an exception to the caller via std::promise or by modifying shared variables (which may require synchronization, see std::mutex and std::atomic).
Use a shared variable and remember about locking. There's even an example in cppreference execution policies
that increments an integer, with a bit of tweaking your code may look like:
std::mutex m;
std::vector<std::exception> throwed;
std::for_each(std::execution::par_unseq, std::begin(a), std::end(a), [&](int) {
std::this_thread::sleep_for(2s);
{
std::lock_guard<std::mutex> guard(m);
if (toBeCancelled) {
throwed.push_back(std::runtime_error("taskCancelled"));
}
}
});
or you can catch the exception and push_back
it then.
Upvotes: 0
Reputation: 63039
You can't let an exception out of the callable you pass to for_each
, under pain of std::terminate
, as you have seen. But you don't need an exception, you know that you are cancelling tasks.
[&](const int& x) {
std::this_thread::sleep_for(2s);
if (toBeCancelled)
return;
}
Aside: in C++20 we get std::stop_token
which is intended for this sort of signalling.
Upvotes: 2