Based on How to implement timeout for function in c++, I wrote this wrapper:
template <typename t_time, typename t_function, typename... t_params>
inline std::conditional_t<
// if 't_function' return type is 'void'
std::is_void_v<std::invoke_result_t<t_function, const bool &, t_params...>>,
// the 'execute' wrapper will return 'bool', which will be 'true' if the
// 'p_function' executes in less 'p_max_time', or 'false' otherwise
bool,
// else it will result a 'std::optional' with the return type of
// 't_function', which will contain a value of that type, if the
// 'p_function' executes in less 'p_max_time', or empty otherwise
std::optional<std::invoke_result_t<t_function, const bool &, t_params...>>>
execute(t_time p_max_time, t_function &p_function, t_params &&... p_params) {
std::mutex _mutex;
std::condition_variable _cond;
bool _timeout{false};
typedef typename std::invoke_result_t<t_function, const bool &, t_params...>
t_ret;
if constexpr (std::is_void_v<t_ret>) {
std::thread _th([&]() -> void {
p_function(_timeout, std::forward<t_params>(p_params)...);
_cond.notify_one();
});
std::unique_lock<std::mutex> _lock{_mutex};
if (_cond.wait_for(_lock, p_max_time) != std::cv_status::timeout) {
_th.join();
return true;
}
_timeout = true;
_th.detach();
return false;
} else {
t_ret _ret;
std::thread _th([&]() -> void {
_ret = p_function(_timeout, std::forward<t_params>(p_params)...);
_cond.notify_one();
});
std::unique_lock<std::mutex> _lock{_mutex};
if (_cond.wait_for(_lock, p_max_time) != std::cv_status::timeout) {
_th.join();
return {std::move(_ret)};
}
_timeout = true;
_th.detach();
return {};
}
}
Unlike the code in the answers for the question I referenced, I would not like to throw an exception in the execute wrapper. If p_function does not return, the wrapper will return a bool, true if p_function executed in at most p_max_time, false otherwise. If p_function returns T, the wrapper will return std::optional<T> which will have a value if p_function does not exceed p_max_time, or it will be empty otherwise.
The const bool & parameter required for p_function is used to inform p_function that its execution exceeded p_max_time, so p_function may stop its execution, though execute will not count on it.
Here is an example:
auto _function = [](const bool &p_is_timeout, int &&p_i) -> void {
std::this_thread::sleep_for(1s);
if (p_is_timeout) {
std::cout << "timeout";
} else {
std::cout << "i = " << p_i << '\n';
}
};
int _i{4};
if (!async::execute(200ms, _function, std::move(_i))) {
std::cout << "TIMEOUT!!\n";
}
So, the problem is _th.detach() causes crash when I execute some test functions in a row. I if change it to _th.join(), the crash no longer occurs, but, obviously, the function that calls the wrapper has to wait for p_function to end, which is not desired.
How can I make execute detach _th thread, without causing crash?