userver: userver/concurrent/variable.hpp Source File
Loading...
Searching...
No Matches
variable.hpp
1#pragma once
2
3#include <cstdlib>
4#include <mutex>
5#include <optional>
6#include <shared_mutex> // for shared_lock
7
8#include <userver/engine/mutex.hpp>
9
10USERVER_NAMESPACE_BEGIN
11
12/// Locking stuff
13namespace concurrent {
14
15/// Proxy class for locked access to data protected with locking::SharedLock<T>
16template <typename Lock, typename Data>
17class LockedPtr final {
18 public:
19 using Mutex = typename Lock::mutex_type;
20
21 LockedPtr(Mutex& mutex, Data& data) : lock_(mutex), data_(data) {}
22 LockedPtr(Lock&& lock, Data& data) : lock_(std::move(lock)), data_(data) {}
23
24 Data& operator*() & { return data_; }
25 const Data& operator*() const& { return data_; }
26
27 /// Don't use *tmp for temporary value, store it to variable.
28 Data& operator*() && { return *GetOnRvalue(); }
29
30 Data* operator->() & { return &data_; }
31 const Data* operator->() const& { return &data_; }
32
33 /// Don't use tmp-> for temporary value, store it to variable.
34 Data* operator->() && { return GetOnRvalue(); }
35
36 Lock& GetLock() { return lock_; }
37
38 private:
39 const Data* GetOnRvalue() {
40 static_assert(!sizeof(Data),
41 "Don't use temporary LockedPtr, store it to a variable");
42 std::abort();
43 }
44
45 Lock lock_;
46 Data& data_;
47};
48
49/// @ingroup userver_concurrency userver_containers
50///
51/// Container for shared data protected with a mutex of any type
52/// (mutex, shared mutex, etc.).
53/// ## Example usage:
54///
55/// @snippet concurrent/variable_test.cpp Sample concurrent::Variable usage
56///
57/// @see @ref scripts/docs/en/userver/synchronization.md
58template <typename Data, typename Mutex = engine::Mutex>
59class Variable final {
60 public:
61 template <typename... Arg>
62 Variable(Arg&&... arg) : data_(std::forward<Arg>(arg)...) {}
63
64 LockedPtr<std::unique_lock<Mutex>, Data> UniqueLock() {
65 return {mutex_, data_};
66 }
67
68 LockedPtr<std::unique_lock<Mutex>, const Data> UniqueLock() const {
69 return {mutex_, data_};
70 }
71
72 std::optional<LockedPtr<std::unique_lock<Mutex>, const Data>> UniqueLock(
73 std::try_to_lock_t) const {
74 return DoUniqueLock(*this, std::try_to_lock);
75 }
76
77 std::optional<LockedPtr<std::unique_lock<Mutex>, Data>> UniqueLock(
78 std::try_to_lock_t) {
79 return DoUniqueLock(*this, std::try_to_lock);
80 }
81
82 std::optional<LockedPtr<std::unique_lock<Mutex>, const Data>> UniqueLock(
83 std::chrono::milliseconds try_duration) const {
84 return DoUniqueLock(*this, try_duration);
85 }
86
87 std::optional<LockedPtr<std::unique_lock<Mutex>, Data>> UniqueLock(
88 std::chrono::milliseconds try_duration) {
89 return DoUniqueLock(*this, try_duration);
90 }
91
92 LockedPtr<std::shared_lock<Mutex>, const Data> SharedLock() const {
93 return {mutex_, data_};
94 }
95
96 /// Useful for grabbing a reference to an object in a node-based container,
97 /// e.g. `std::unordered_map`. Values must support concurrent modification.
98 LockedPtr<std::shared_lock<Mutex>, Data> SharedMutableLockUnsafe() {
99 return {mutex_, data_};
100 }
101
102 LockedPtr<std::lock_guard<Mutex>, Data> Lock() { return {mutex_, data_}; }
103
104 LockedPtr<std::lock_guard<Mutex>, const Data> Lock() const {
105 return {mutex_, data_};
106 }
107
108 /// Get raw mutex. Use with caution. For simple use cases call Lock(),
109 /// UniqueLock(), SharedLock() instead.
110 Mutex& GetMutexUnsafe() const { return mutex_; }
111
112 /// Get raw data. Use with extreme caution, only for cases where it is
113 /// impossible to access data with safe methods (e.g. std::scoped_lock with
114 /// multiple mutexes). For simple use cases call Lock(), UniqueLock(),
115 /// SharedLock() instead.
116 Data& GetDataUnsafe() { return data_; }
117
118 const Data& GetDataUnsafe() const { return data_; }
119
120 private:
121 mutable Mutex mutex_;
122 Data data_;
123
124 /// We need this function to work around const/non-const methods. This
125 /// helper accepts concurrent variables as template parameter and thus
126 /// will accept both const and non-const vars. And Data typename will
127 /// resolve to const/non-const accordingly.
128 template <typename VariableType, typename... StdUniqueLockArgs>
129 static auto DoUniqueLock(VariableType& concurrent_variable,
130 StdUniqueLockArgs&&... args) {
131 std::unique_lock<Mutex> lock(concurrent_variable.mutex_,
132 std::forward<StdUniqueLockArgs>(args)...);
133 return lock ? std::optional{LockedPtr{std::move(lock),
134 concurrent_variable.data_}}
135 : std::nullopt;
136 }
137};
138
139} // namespace concurrent
140
141USERVER_NAMESPACE_END