userver: compiler::ThreadLocal< VariableType, Factory > Class Template Reference
Loading...
Searching...
No Matches
compiler::ThreadLocal< VariableType, Factory > Class Template Referencefinal

#include <userver/compiler/thread_local.hpp>

Detailed Description

template<typename VariableType, typename Factory>
class compiler::ThreadLocal< VariableType, Factory >

Creates a unique thread-local variable that can be used in a coroutine-safe manner.

Thread-local variables are known to cause issues when used together with userver coroutines:

Thread-local variables created through this class are protected against these issues.

ThreadLocal should be passed a factory function that constructs the variable. Example usage:

// NOTE: the thread-local itself should be:
// * NEVER `thread_local`
// * `static` where appropriate
// * `inline` where appropriate
compiler::ThreadLocal local_buffer = [] { return std::string{}; };
// If we just write one item in `data` after the other, we might perform too
// many syscall-s, because each of the strings may be short.
//
// So let's somehow concatenate `data` into a single string. But now there
// may be too many allocations if such an operation is frequent.
//
// Solution: use a thread_local buffer to only allocate once per thread.
// NOTE: the scope should be non-static, non-thread_local.
auto buffer = local_buffer.Use();
buffer->clear();
for (const auto piece : data) {
buffer->append(piece);
}
fd.Write(*buffer);
}

An example with slightly more complex initialization for the variable:

// NOTE: if you need thread-local randomness, don't roll your own!
// See <userver/utils/rand.hpp> and <userver/crypto/random.hpp> first!
compiler::ThreadLocal local_rng = [] { return std::minstd_rand{utils::Rand()}; };
std::uint32_t MyRand() {
auto rng = local_rng.Use();
return std::uniform_int_distribution<std::uint32_t>{}(*rng);
}

Once acquired through Use, the reference to the thread-local variable should not be returned or otherwise escape the scope of the ThreadLocalScope object. An example of buggy code:

compiler::ThreadLocal local_buffer = [] { return std::string{}; };
std::string_view PrepareBuffer(std::string_view x, std::string_view y) {
const auto buffer = local_buffer.Use();
buffer->clear();
buffer->append(x);
buffer->append(y);
return *buffer; // <- BUG! Reference to thread-local escapes the scope
}

Do not store a reference to the thread-local object in a separate variable:

compiler::ThreadLocal local_buffer = [] { return std::string{}; };
void WriteMessage(std::string_view x, std::string_view y) {
auto buffer_scope = local_buffer.Use();
// Code smell! This makes it more difficult to see that `buffer` is
// a reference to thread-local at a glance.
auto& buffer = *buffer_scope;
buffer.clear();
// ...
}

Until the variable name goes out of scope, userver engine synchronization primitives and clients (web or db) should not be used.

Definition at line 128 of file thread_local.hpp.

Public Member Functions

consteval ThreadLocal (Factory factory)
 
ThreadLocalScope< VariableType > Use ()
 

Constructor & Destructor Documentation

◆ ThreadLocal() [1/2]

template<typename VariableType, typename Factory>
compiler::ThreadLocal< VariableType, Factory >::ThreadLocal ( )
inlineconsteval

Definition at line 133 of file thread_local.hpp.

◆ ThreadLocal() [2/2]

template<typename VariableType, typename Factory>
compiler::ThreadLocal< VariableType, Factory >::ThreadLocal ( Factory factory)
inlineconsteval

Definition at line 137 of file thread_local.hpp.

Member Function Documentation

◆ Use()

template<typename VariableType, typename Factory>
ThreadLocalScope< VariableType > compiler::ThreadLocal< VariableType, Factory >::Use ( )
inline

Definition at line 141 of file thread_local.hpp.


The documentation for this class was generated from the following file: