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