namespace tf { /** @page RequestCancellation Request Cancellation This chapters discusses how to cancel submitted tasks. @tableofcontents @section CancelARunningTaskflow 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. @code{.cpp} 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 fu = executor.run(taskflow); // request to cancel the above submitted execution fu.cancel(); // wait until the cancellation completes fu.get(); @endcode @note tf::Future::cancel is @em non-deterministic and @em 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 @c 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: @code{.cpp} 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 @endcode The undefined behavior problem exists because tf::Future::cancel does not guarantee an immediate cancellation. To fix the problem, call @c get to ensure the cancellation completes before the end of the scope destroys the taskflow. @code{.cpp} 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 } @endcode @section UnderstandTheLimitationsOfCancellation 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. */ }