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?