parts/6.59.Concurrency-Activation-CGA.md

6.59 Concurrency – Activation [CGA]

6.59.1 Applicability to language

The vulnerabilities as specified in ISO/IEC 24772-1 clause 6.59 do not apply to C++, as long as the standard library facilities for creating threads are used. If the C standard library mechanisms or threading libraries from POSIX or other applications are used to create threads, then the vulnerabilities apply as described in ISO/IEC 24772-3 clause 6.59. This subclause will examine the C++ standard mechanisms.

Creating a thread using the std::async function or the std::thread or std::jthread results in the constructor synchronized with the thread creation site. Unless the thread is detached, join() must be called when using std::thread or std::terminate will be invoked upon destruction of the thread object. This can be avoided by using std::jthread instead.

Failure to create or start a thread due to lack of system resources will cause an exception to be thrown to the creating thread thus the thread object never exists. For the vulnerabilities with unhandled exceptions see clause 6.36 Ignored error status and unhandled exceptions [OYB].

C++ provides other ways to create parallel executing functions via the std::async() call or the std::packaged_task() functions. If std::async() threads are created without a launch policy, then system determines whether or not the thread is run lazily (in the current thread) or in a new thread. The launch policy std::launch::async or std::launch::deferred controls whether each asynch thread creation creates new threads or executes the call on demand. When a launch policy is std::launch::async then each call to std::async() will always create a new thread which can lead to resource exhaustion. When a launch policy is std::launch::deferred() the function will execute on the thread that executes the respective get() function for that async function.

C++ provides a mechanism to construct and control the execution of a function on a user-controlled potentially asynchronous thread via the std::packaged_task mechanism that allows access to the result via a std::future object. Each packaged_task object can be executed at most once and can be executed on the current thread or on a different thread.

There are a number of vulnerabilities that are possible once the thread is created using std::thread or std::jthread. See 6.63 lock protocol errors [CGM]. Each std::thread object must have its .join() member function called before the object is destroyed unless its .detach() member function was previously called. This is not required for threads created as std::jthread. If .detach() was called on thread object then any calls to .join() will result in an exception.

Any exception thrown within a thread’s function needs to be handled by that thread, otherwise such an exception will cause program termination. For handling such termination see clause 6.62 Concurrency - Premature termination [CGS]. Employing a std::packaged_task object as the thread function allows to pass an exception to the holder of its std::future object.

A detached thread will execute until its thread function ends or until the program is terminated. The std::jthread type allows to employ cooperative thread termination through std::stop_token See 6.60 Concurrency – Directed termination [CGT] or 6.62 Concurrency – Premature termination [CGS].

6.59.2 Avoidance mechanisms for language users

To avoid the vulnerability or mitigate its ill effects, C++ software developers can: