Support auto-canceled tasks

This commit is contained in:
Nikita Lisitsa 2020-11-21 15:43:42 +03:00
parent 5f91587a39
commit a6b931e8e2

View file

@ -17,6 +17,11 @@ namespace psemek::util
{ {
std::promise<T> promise; std::promise<T> promise;
std::atomic<bool> canceled = false; std::atomic<bool> canceled = false;
bool const auto_cancel;
task_state(bool auto_cancel)
: auto_cancel{auto_cancel}
{}
}; };
template <typename T, typename F, typename ... Args> template <typename T, typename F, typename ... Args>
@ -52,14 +57,38 @@ namespace psemek::util
char const * what() const noexcept { return "task canceled"; } char const * what() const noexcept { return "task canceled"; }
}; };
struct auto_cancel_tag{};
constexpr auto_cancel_tag auto_cancel;
template <typename T> template <typename T>
struct future struct future
{ {
future() = default;
future(std::shared_ptr<detail::task_state<T>> state) future(std::shared_ptr<detail::task_state<T>> state)
: state_(std::move(state)) : state_(std::move(state))
, f_(state_->promise.get_future()) , f_(state_->promise.get_future())
{} {}
~future()
{
reset();
}
void reset()
{
if (state_ && state_->auto_cancel)
cancel();
state_.reset();
f_ = std::future<T>();
}
explicit operator bool() const
{
return static_cast<bool>(state_);
}
bool wait() const bool wait() const
{ {
f_.wait(); f_.wait();
@ -80,14 +109,15 @@ namespace psemek::util
T get() T get()
{ {
if (state_->canceled) if (state_ && state_->canceled)
throw canceled_task_error{}; throw canceled_task_error{};
return f_.get(); return f_.get();
} }
void cancel() void cancel()
{ {
state_->canceled = true; if (state_)
state_->canceled = true;
} }
bool ready() const bool ready() const
@ -143,11 +173,21 @@ namespace psemek::util
template <typename F, typename ... Args> template <typename F, typename ... Args>
auto dispatch(F && f, Args && ... args); auto dispatch(F && f, Args && ... args);
// Post a callable for execution. Retuns a future.
// The task cancels on future destruction.
template <typename F, typename ... Args>
auto dispatch(auto_cancel_tag, F && f, Args && ... args);
// Post a callable for execution at a certain time point. // Post a callable for execution at a certain time point.
// Retuns a future. // Retuns a future.
template <typename TimePoint, typename F, typename ... Args> template <typename TimePoint, typename F, typename ... Args>
auto dispatch_at(TimePoint time, F && f, Args && ... args); auto dispatch_at(TimePoint time, F && f, Args && ... args);
// Post a callable for execution at a certain time point.
// Retuns a future. The task cancels on future destruction.
template <typename TimePoint, typename F, typename ... Args>
auto dispatch_at(TimePoint time, auto_cancel_tag, F && f, Args && ... args);
virtual ~executor() {} virtual ~executor() {}
}; };
@ -155,7 +195,16 @@ namespace psemek::util
auto executor::dispatch(F && f, Args && ... args) auto executor::dispatch(F && f, Args && ... args)
{ {
using R = decltype(f()); using R = decltype(f());
auto state = std::make_shared<detail::task_state<R>>(); auto state = std::make_shared<detail::task_state<R>>(false);
post(detail::wrap_task(state, std::forward<F>(f), std::forward<Args>(args)...));
return future<R>(state);
}
template <typename F, typename ... Args>
auto executor::dispatch(auto_cancel_tag, F && f, Args && ... args)
{
using R = decltype(f());
auto state = std::make_shared<detail::task_state<R>>(true);
post(detail::wrap_task(state, std::forward<F>(f), std::forward<Args>(args)...)); post(detail::wrap_task(state, std::forward<F>(f), std::forward<Args>(args)...));
return future<R>(state); return future<R>(state);
} }
@ -164,7 +213,16 @@ namespace psemek::util
auto executor::dispatch_at(TimePoint time, F && f, Args && ... args) auto executor::dispatch_at(TimePoint time, F && f, Args && ... args)
{ {
using R = decltype(f()); using R = decltype(f());
auto state = std::make_shared<detail::task_state<R>>(); auto state = std::make_shared<detail::task_state<R>>(false);
post_at(std::chrono::time_point_cast<clock::duration>(time), detail::wrap_task(state, std::forward<F>(f), std::forward<Args>(args)...));
return future<R>(state);
}
template <typename TimePoint, typename F, typename ... Args>
auto executor::dispatch_at(TimePoint time, auto_cancel_tag, F && f, Args && ... args)
{
using R = decltype(f());
auto state = std::make_shared<detail::task_state<R>>(true);
post_at(std::chrono::time_point_cast<clock::duration>(time), detail::wrap_task(state, std::forward<F>(f), std::forward<Args>(args)...)); post_at(std::chrono::time_point_cast<clock::duration>(time), detail::wrap_task(state, std::forward<F>(f), std::forward<Args>(args)...));
return future<R>(state); return future<R>(state);
} }