rspec/rules/S6367/cfamily/rule.adoc

55 lines
2.3 KiB
Plaintext

== Why is this an issue?
In contrast to normal functions, coroutines can suspend and later resume their execution. Depending on the program, the coroutine may resume on a different thread of execution than the one it was started or run previously on.
Therefore, the access to the "same" variable with `thread_local` storage may produce different values, as illustrated below:
[source,cpp]
----
thread_local std::vector<Decorator> decorators;
lazy<Thingy> doSomething() {
// evaluation started on thread t1
/* .... */
const std::size_t decoratorCount = decorators.size(); // value specific to thread t1
auto result = co_await produceThingy();
// after co_await, execution resumes on thread t2
for (std::size_t i = 0; i < decoratorCount; ++i) {
decorators[i].modify(result); // access value specific to t2
// miss some tasks if t1:decorators.size() < t2:decorators.size()
// undefined behavior if t1:decorators.size() > t2:decorators.size()
}
co_return result;
}
----
This behavior is surprising and unintuitive compared to normal functions that are always evaluated on a single thread.
The same issue can happen for the use of different `thread_local` variables if their values are interconnected (e.g., one is the address of the buffer, and the other is the number of elements in the buffer).
Moreover, access to `thread_local` variables defined inside the coroutine may read uninitialized memory.
Each such variable is initialized when a specific thread enters the function for the first time,
and if the function was never called from a thread on which the coroutine is resumed, it is uninitialized.
This rule raises an issue on the declaration of `thread_local` variables and access to `thread_local` variables
in coroutines.
=== Noncompliant code example
[source,cpp]
----
thread_local std::vector<Decorator> decorators;
lazy<Thingy> doSomething() {
thread_local Decorator localDecorator; // Noncompliant
const std::size_t decoratorCount = decorators.size(); // Noncompliant
/* ... */
auto result = co_await produceThingy();
for (std::size_t i = 0; i < taskCount; ++i) {
decorators[i].modify(result);
}
localDecorator.modify(result); // Noncompliant
co_return result;
}
----
== Resources
* {cpp} reference - https://en.cppreference.com/w/cpp/language/storage_duration[Storage class specifiers: thread_local]