-
Problem **: chcę napisać wrapper na dowolną funkcję, który to
-
wykonuje tę funkcję
-
mierzy czas jej wykonania
-
pobiera zwracaną przez nią wartość
-
kończy funkcję kiedy minie określony czas a ona sama się nie skończyła. Taki
timeout, odpalam funkcję, ma ona określony czas na wykonanie, jeśli nie zdąży, to przerywam jej działanie. wrapper ma skończyć działanie i zwrócić rezultat w momencie, kiedy jeden z warunków 1) funkcja skończyła działanie i zwróciła wynik 2) osiągniętotimeoutzostanie spełniony -
Założenia **: mogę zrobić
std::thread::detach()na wątku wykonującym funkcję, nie mogę zrobić na nimstd::terminate(), nie mam dostępu do funkcji. To jest jakaś funkcja z jakimiś argumentami, pojęcia nie mam co dzieje się w środku i nie mogę tego modyfikować. Nie chciałbym używać czegoś ponad C++11. -
Próby rozwiązania **:
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;
}
http://melpon.org/wandbox/permlink/4eopMBWg2RTfVtGC
niby wszystko działa dobrze, ale: po rzuceniu wyjątku std::runtime_error referencje przekazane do lambdy w std::thread::thread() są zabijane a funkcja działa dalej na nich. Mogę użyć std::shared_ptr, ale całość jest brzydka i nie dam sobie ręki uciąć, że nie ma tam 10 UB.
*
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();
}
}
fajne i ładne, natomiast nie przerywa działania po timeout, bo w std::async() tworzy się std::thread a w std::~async() jest robione coś jak std::thread::join() na tym stworzonym wątku. Głupie. http://melpon.org/wandbox/permlink/Cyd2hYJCQSIxETqL
*
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));
if (handle.wait_for(max_time) == std::future_status::timeout) {
th.detach();
throw std::runtime_error("timeout");
} else {
th.detach();
return handle.get();
}
}
to też działa http://melpon.org/wandbox/permlink/FoCdp6K8CnlmdJYi ale jestem przekonany, że robienie std::thread::detach() ma wątku z future to gwarantowane UB. Mam jednak wrażenie, że rozwiązanie poprawne jest najbliższe temu.