C++20 Concurrency: <stop_token>

A simple way to handle cancellation of asynchronous tasks

CMP

--

In a previous article, I talked about using std::jthread as a simpler and safer way to create multi-threaded C++ programs using C++20. However, I did not discuss the benefits of using the C++20 <stop_token> library header alongside std::jthread for simple cancellation of coroutines and asynchronous tasks.

What is the <stop_token> header used for?

This header contains a few useful components, including std::stop_token, std::stop_source, and std::stop_callback. Simply put, a std::stop_source represents a request to stop a std::jthread, while a std::stop_token allows you to query if a cancellation request has been made. If you wish to register a callback function to execute when a std::jthread is stopped, you can do so with std::stop_callback.

How to use std::stop_token ?

A std::stop_token object is not constructed directly; instead, it is retrieved from a std::stop_source, either directly or indirectly via a std::jthread, which holds an internal std::stop_source private member. Below is a simple example modified from the C++ reference demonstrating the usage with std::jthread:

#include <iostream>
#include <stop_token>
#include <thread>

using namespace std::literals::chrono_literals;

void printIncrementingValues(std::stop_token stopToken, int value) {
while (!stopToken.stop_requested()) {
std::cout << value++ << " " << std::flush;
std::this_thread::sleep_for(500ms);
}
std::cout << std::endl;
}

int main() {
// Print incrementing values starting with 1 for 5 seconds
std::jthread thread(printIncrementingValues, 1);
std::this_thread::sleep_for(5s);

// Destructor of std::jthread will call request_stop() and join(),
// but they can be explicitly called as well if needed for specific
// synchronization scenarios
thread.request_stop();
thread.join();

return 0;
}
Possible output:

1 2 3 4 5 6 7 8 9 10

In this example, a std::jthread was created to run the printIncrementingValues worker function with a single int parameter. However, notice that the worker function takes two parameters: a std::stop_token and an int! As strange as it may seem, you do not actually construct and pass in a std::stop_token, you simply add it as the first parameter argument to a function.

--

--

CMP

Software engineer specializing in operating systems, navigating the intracicies of the C++ language and systems programming.