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 = impl::UniqueDefaultFactory<VariableType>>
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.

Example usage:

// NOTE: the thread-local itself should be:
// * NEVER `thread_local`
// * `static` where appropriate
// * `inline` where appropriate
void WriteAll(fs::blocking::FileDescriptor& fd,
// 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);
}

The thread-local variable is value-initialized.

In C++17 mode, or if you need to initialize the variable with some arguments, the ThreadLocal should be passed a capture-less lambda that constructs the variable. Example:

// 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:

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:

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 138 of file thread_local.hpp.

Public Member Functions

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

Constructor & Destructor Documentation

◆ ThreadLocal() [1/2]

template<typename VariableType , typename Factory = impl::UniqueDefaultFactory<VariableType>>
USERVER_IMPL_CONSTEVAL compiler::ThreadLocal< VariableType, Factory >::ThreadLocal ( )
inline

Definition at line 144 of file thread_local.hpp.

◆ ThreadLocal() [2/2]

template<typename VariableType , typename Factory = impl::UniqueDefaultFactory<VariableType>>
USERVER_IMPL_CONSTEVAL compiler::ThreadLocal< VariableType, Factory >::ThreadLocal ( Factory factory)
inline

Definition at line 146 of file thread_local.hpp.

Member Function Documentation

◆ Use()

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

Definition at line 149 of file thread_local.hpp.


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