Reputation: 43
Consider following code:
template <typename func_t, typename ... Args>
auto waiter (func_t func, const Args &... args) -> decltype(func(args...)) {
const static std::chrono::milliseconds max_time(10);
auto task = std::packaged_task<decltype(func(args...))()>(std::bind(func, args...));
auto handle = task.get_future();
std::thread th(std::move(task)); // here
if (handle.wait_for(max_time) == std::future_status::timeout) {
th.detach(); // and here
throw std::runtime_error("timeout");
} else { // and here
th.detach();
return handle.get();
}
}
Can I safely take inner std::thread
from std::future
and detach it? I guess it's obvious UB. waiter()
function is designed to take any callable object, call it and stop work if any of
is true. I tried to do this in many ways, but there is always a problem. I realized that the problem is C++14 don't have process support. First attempt:
template <typename func_t, typename ... Args>
auto waiter (func_t func, const Args &... args) -> decltype(func(args...)) {
const static std::chrono::milliseconds max_time(10);
decltype(func(args...)) result;
std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx);
std::condition_variable cv;
thread th([&] {
result = func(args...);
cv.notify_all();
});
auto waiting_result = cv.wait_for(lock, max_time);
th.detach();
if (waiting_result == std::cv_status::timeout) {
throw std::runtime_error("timeout");
}
return result;
}
The problem here is after throwing exception, std::thread
th
is still running with killed references. Second attempt:
template <typename func_t, typename ... Args>
auto waiter (func_t func, const Args &... args) -> decltype(func(args...)) {
const static std::chrono::milliseconds max_time(10);
auto handle = std::async(std::launch::async, func, args ...);
if (handle.wait_for(max_time) == std::future_status::timeout) {
throw std::runtime_error("timeout");
} else {
return handle.get();
}
}
Here the problem is (as far I understand) that std::async::~async()
calls some kind of std::thread::join()
on it's inner thread and the main thread is still waiting for std::async
. http://melpon.org/wandbox/permlink/Cyd2hYJCQSIxETqL
Silly one.
Upvotes: 2
Views: 1814
Reputation: 15524
You can detach the thread like you did in your first attempt, but then, as you say, the thread will keep running detached until it finishes on its own.
The problem with the second attempt is that the std::future
returned by std::async
is kind of special as its destructor will block until the thread finishes execution (unlike the destructor of a regular std::future
)
I'm afraid that in C++ it's not possible to safely terminate a single thread without killing all threads. The problem arises from the difficulty of safely unwinding the stack at an arbitrary point during execution. If let's say, the unwinding should take place in an inappropriate place like a ctor or dtor, it would break the rules of object-storage duration.
See this answer for more details.
Upvotes: 1