userver: userver/utils/atomic.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
atomic.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/atomic.hpp
4/// @brief Helper algorithms to work with atomics
5/// @ingroup userver_universal userver_concurrency
6
7#include <atomic>
8
9USERVER_NAMESPACE_BEGIN
10
11namespace utils {
12
13/// @ingroup userver_concurrency
14///
15/// @brief Atomically performs the operation of `updater` on `atomic`
16/// @details `updater` may be called multiple times per one call of
17/// `AtomicUpdate`, so it must be idempotent. To ensure that the function does
18/// not spin for a long time, `updater` must be fairly simple and fast.
19/// @param atomic the variable to update
20/// @param updater a lambda that takes the old value and produces the new value
21/// @return The updated value
22/// @note Uses std::memory_order_relaxed
23template <typename T, typename Func>
24T AtomicUpdate(std::atomic<T>& atomic, Func updater) {
25 T old_value = atomic.load();
26 while (true) {
27 // make a copy to to keep old_value unchanged
28 const T new_value = updater(T{old_value});
29
30 // don't mark cache line as dirty
31 if (old_value == new_value) return old_value;
32
33 if (atomic.compare_exchange_weak(old_value, new_value)) return new_value;
34 }
35}
36
37/// @ingroup userver_concurrency
38///
39/// @brief Concurrently safe sets `atomic` to a `value` if `value` is less
40///
41/// @note Uses std::memory_order_relaxed
42template <typename T>
43T AtomicMin(std::atomic<T>& atomic, T value) {
44 return utils::AtomicUpdate(atomic, [value](T old_value) {
45 return value < old_value ? value : old_value;
46 });
47}
48
49/// @ingroup userver_concurrency
50///
51/// @brief Concurrently safe sets `atomic` to a `value` if `value` is greater
52///
53/// @note Uses std::memory_order_relaxed
54template <typename T>
55T AtomicMax(std::atomic<T>& atomic, T value) {
56 return utils::AtomicUpdate(atomic, [value](T old_value) {
57 return old_value < value ? value : old_value;
58 });
59}
60
61} // namespace utils
62
63USERVER_NAMESPACE_END