2023-05-03 11:06:20 +02:00
== Why is this an issue?
2021-12-01 14:13:36 +00:00
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.
2023-09-25 14:09:50 +02:00
Therefore, the access to the "same" variable with `thread_local` storage may produce different values, as illustrated below:
[source,cpp]
2021-12-01 14:13:36 +00:00
----
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()
2023-09-25 14:09:50 +02:00
}
2021-12-01 14:13:36 +00:00
co_return result;
}
----
This behavior is surprising and unintuitive compared to normal functions that are always evaluated on a single thread.
2023-09-25 14:09:50 +02:00
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).
2021-12-01 14:13:36 +00:00
2023-09-25 14:09:50 +02:00
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,
2021-12-01 14:13:36 +00:00
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.
2023-05-03 11:06:20 +02:00
=== Noncompliant code example
2021-12-01 14:13:36 +00:00
2022-02-04 17:28:24 +01:00
[source,cpp]
2021-12-01 14:13:36 +00:00
----
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);
2023-09-25 14:09:50 +02:00
}
2021-12-01 14:13:36 +00:00
localDecorator.modify(result); // Noncompliant
co_return result;
}
----
2023-09-25 14:09:50 +02:00
== Resources
* {cpp} reference - https://en.cppreference.com/w/cpp/language/storage_duration[Storage class specifiers: thread_local]