This vulnerability as specified in ISO/IEC 24772-1 clause 6.60 is mitigated in C++, as long as the standard C++ library facilities for threads are used. For issues associated with threades created using the standard C library, see ISO/IEC 24772-3 clause 6.60.
C++ does not provide the means to terminate a C++ thread asynchronously, except in the case that std::terminate() is called. Instead C++ allows cooperative termination through the use of std::stop_token
, however, a thread instructed by a stop request to cease execution can ignore such a request. For example, using std::jthread::request_stop()
to send a stop request to the started thread, the created thread can have a thread function that never handles such a stop request.
void some_function(int value);
void stoppable_function(std::stop_token tok, int value);
int main()
{using namespace std;
std::jthread t(some_function,10); // stop_token ignored since not provided
std::jthread t2(stoppable_function,100); // stop_token passed
// no-op as ignored in t
t.request_stop(); // stop_token `tok`{.cpp} signalled in `t2`{.cpp},
t2.request_stop(); // returns `true`{.cpp} showing `stop_token`{cpp} set
}
void stoppable_function(std::stop_token tok, int value) {
using namespace std::literals;
while (!tok.stop_requested()) //
{std::osyncstream(std::cout) << value++ << '\n';
std::this_thread::sleep_for(200ms);
}
}
void some_function(int value)
{ using namespace std::literals;
for (int i =0; i<10; ++i)
{std::osyncstream(std::cout) << value++ << '\n';
std::this_thread::sleep_for(200ms);
}
}```{.cpp}
bool OK = t2.request_stop(); }
void stoppable_function(std::stop_token tok, int value) {
while (!stop_token.stop_requested()) //
{std::cout << value++ << ' ' << std::flush;
std::this_thread::sleep_for(200ms);
}void some_function(int value) {
for (int i = 0; i<10; i++) //
{std::cout << value++ << ' ' << std::flush;
std::this_thread::sleep_for(200ms);
}
std::cout << std::endl;
}
Note that in the while loop in ’stoppable_function(){.cpp}, if stop_token.stop_requested had not been checked, it would continue to run until the program itself is terminated. In the above example, at the end of the main function the destructors of the thread objects
t{.cpp} and
t2{.cpp} will call
this->request_stop(){.cpp} and
this->join(){.cpp}. If one of the thread functions never returns, then the corresponding
join()`{.cpp} call will block.
Other programmed mechanisms can be constructed to cause another thread to complete, such as setting a shared variable to a known value that the target thread reads and then terminates itself.
A common programming mistake is to create threads with the intention that they will participate in a joint computation, but missing the join()
operation in one or more threads. If jthreads
are used for the threading, all threads are joined with the thread that created them.
Even if a thread is successfully terminated, the thread that initiated the termination should take action to clean up after the terminating thread,such as releasing memory
Discussion: Do we need to discuss termination of tasks?
To avoid the vulnerability or mitigate its ill effects, C++ software developers can:
For threads created using the C library, follow the avoidance mechanisms of ISO/IEC TR 24772-3.
Follow the avoidance mechanisms of ISO/IEC 24772-1 clause 6.60.5.
Use code reviews and static analysis tools if available, to ensure that threads correctly terminate.