Taskflow  3.2.0-Master-Branch
Loading...
Searching...
No Matches
Request Cancellation

This chapters discusses how to cancel submitted tasks.

Cancel Execution of Taskflows

When you submit a taskflow to an executor (e.g., tf::Executor::run), the executor returns a tf::Future object that will hold the result of the execution. tf::Future is a derived class from std::future. In addition to base methods of std::future, you can call tf::Future::cancel to cancel the execution of a running taskflow. The following example cancels a submission of a taskflow that contains 1000 tasks each running one second.

tf::Executor executor;
tf::Taskflow taskflow;
for(int i=0; i<1000; i++) {
taskflow.emplace([](){
});
}
// submit the taskflow
tf::Future fu = executor.run(taskflow);
// request to cancel the above submitted execution
fu.cancel();
// wait until the cancellation completes
fu.get();
class to create an executor for running a taskflow graph
Definition executor.hpp:50
tf::Future< void > run(Taskflow &taskflow)
runs a taskflow once
Definition executor.hpp:1573
Task emplace(C &&callable)
creates a static task
Definition flow_builder.hpp:742
class to access the result of an execution
Definition core/taskflow.hpp:571
bool cancel()
cancels the execution of the running taskflow associated with this future object
Definition core/taskflow.hpp:642
class to create a taskflow object
Definition core/taskflow.hpp:73
T sleep_for(T... args)
Note
tf::Future::cancel is non-deterministic and out-of-order.

When you request a cancellation, the executor will stop scheduling the rest tasks of the taskflow. Tasks that are already running will continue to finish, but their successor tasks will not be scheduled to run. A cancellation is considered complete when all these running tasks finish. To wait for a cancellation to complete, you may explicitly call tf::Future::get.

Attention
It is your responsibility to ensure that the taskflow remains alive before the cancellation completes.

For instance, the following code results in undefined behavior:

tf::Executor executor;
{
tf::Taskflow taskflow;
for(int i=0; i<1000; i++) {
taskflow.emplace([](){});
}
tf::Future fu = executor.run(taskflow);
fu.cancel(); // there can still be task running after cancellation
} // destroying taskflow here can result in undefined behavior

The undefined behavior problem exists because tf::Future::cancel does not guarantee an immediate cancellation. To fix the problem, call get to ensure the cancellation completes before the end of the scope destroys the taskflow.

tf::Executor executor;
{
tf::Taskflow taskflow;
for(int i=0; i<1000; i++) {
taskflow.emplace([](){});
}
tf::Future fu = executor.run(taskflow);
fu.cancel(); // there can still be task running after cancellation
fu.get(); // waits until the cancellation completes
}

Cancel Execution of Asynchronous Tasks

You can cancel submitted asynchronous tasks using tf::Future::cancel. The following example launches 10000 asynchronous tasks through an executor, acquires their futures, and cancel all of them.

tf::Executor executor;
// submit 10000 asynchronous tasks
for(int i=0; i<10000; i++) {
futures.push_back(executor.async([i](){
printf("task %d\n", i);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}));
}
// cancel all asynchronous tasks
for(auto& fu : futures) {
fu.cancel();
}
// wait until the 10000 cancellations complete
executor.wait_for_all();
auto async(F &&f, ArgsT &&... args)
runs a given function asynchronously
Definition executor.hpp:784
void wait_for_all()
wait for all tasks to complete
Definition executor.hpp:1709
task 0
task 5
task 7
task 9
task 4
task 1
task 6
task 8
task 2
task 10
task 3
task 11

Similar to cancelling a running taskflow, cancelling a submitted asynchronous task does not guarantee the execution will be cancelled. The result depends on the present available workers and whether the asynchronous task is being run by a worker. To wait for a cancellation to complete, you may explicitly call tf::Future::get. The result may be a std::nullopt if that asynchronous task does not run.

tf::Executor executor;
tf::Future<std::optional<int>> fu = executor.async([](){ return 1; };
fu.cancel();
// call tf::Future::get to wait for the cancellation to complete
if(auto ret = fu.get(); ret == std::nullopt) {
std::cout << "asynchronous task 1 is cancelled\n";
}
else {
std::cout << "asynchronous task 1 returns " << ret.value() << '\n';
}
T forward(T... args)

Understand the Limitations of Cancellation

Canceling the execution of a running taskflow has the following limitations:

  • Cancellation is non-preemptive. A running task will not be cancelled until it finishes.
  • Cancelling a taskflow with tasks acquiring and/or releasing tf::Semaphore results is currently not supported.

We may overcome these limitations in the future releases.