-
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ętotimeout
zostanie 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.
karolinaakarolinaa