Loading...
Searching...
No Matches
Request Cancellation

This chapters discusses how to cancel a running taskflow.

Cancel a Running Taskflow

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

tf::Executor executor;
tf::Taskflow taskflow;
for(int i=0; i<1000; i++) {
taskflow.emplace([](){
std::this_thread::sleep_for(std::chrono::seconds(1));
});
}
// submit the taskflow
tf::Future<void> fu = executor.run(taskflow);
// request to cancel the above submitted execution
fu.cancel();
// wait until the cancellation completes
fu.wait();
class to create an executor
Definition executor.hpp:62
tf::Future< void > run(Taskflow &taskflow)
runs a taskflow once
Task emplace(C &&callable)
creates a static task
Definition flow_builder.hpp:1352
class to access the result of an execution
Definition taskflow.hpp:630
bool cancel()
cancels the execution of the running taskflow associated with this future object
Definition taskflow.hpp:721
class to create a taskflow object
Definition taskflow.hpp:64

When you request a cancellation, the executor will stop scheduling the remaining tasks of the taskflow. Requesting a cancellation does not guarantee an immediate stop of a running taskflow. Tasks that are already running will continue to finish, but their successor tasks will not be scheduled. A cancellation is considered complete only after all running tasks have finished. To wait for the cancellation to complete, you can explicitly call tf::Future::wait. Note that it is your responsibility to ensure that the taskflow remains alive until the cancellation is complete, as there may still be running tasks that cannot be canceled. 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

To avoid this issue, call wait to ensure the cancellation completes before the taskflow is destroyed at the end of the scope.

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.wait(); // wait until the cancellation completes
}

Understand the Limitations of Cancellation

Due to its asynchronous and non-deterministic nature, taskflow cancellation has the following limitations:

  • Non-preemptive behavior: Cancellation does not forcibly terminate running tasks. Any task already in execution will continue to completion before cancellation takes effect.
  • Semaphore incompatibility: Cancelling a taskflow that includes tasks involving tf::Semaphore (i.e., acquiring or releasing) is currently unsupported and may lead to undefined behavior.

We may overcome these limitations in the future releases.